Si usted está pensando en usar de punto flotante para ayudar con la aritmética de enteros, usted tiene que tener cuidado.
Por lo general, trato de evitar los cálculos de FP siempre que sea posible.
Las operaciones de coma flotante no son exactas. Nunca se puede saber con certeza qué evaluará (int)(Math.log(65536)/Math.log(2))
. Por ejemplo, Math.ceil(Math.log(1<<29)/Math.log(2))
es 30 en mi PC donde matemáticamente debería ser exactamente 29. No encontré un valor para x donde (int)(Math.log(x)/Math.log(2))
falla (solo porque solo hay 32 valores "peligrosos"), pero eso no significa que lo hará funciona de la misma manera en cualquier PC.
El truco habitual aquí es usar "épsilon" al redondear. Me gusta (int)(Math.log(x)/Math.log(2)+1e-10)
nunca debería fallar. La elección de este "épsilon" no es una tarea trivial.
Más manifestación, utilizando una tarea más general - tratar de implementar int log(int x, int base)
:
El código de prueba:
static int pow(int base, int power) {
int result = 1;
for (int i = 0; i < power; i++)
result *= base;
return result;
}
private static void test(int base, int pow) {
int x = pow(base, pow);
if (pow != log(x, base))
System.out.println(String.format("error at %d^%d", base, pow));
if(pow!=0 && (pow-1) != log(x-1, base))
System.out.println(String.format("error at %d^%d-1", base, pow));
}
public static void main(String[] args) {
for (int base = 2; base < 500; base++) {
int maxPow = (int) (Math.log(Integer.MAX_VALUE)/Math.log(base));
for (int pow = 0; pow <= maxPow; pow++) {
test(base, pow);
}
}
}
Si utilizamos la implementación más directo de logaritmo,
static int log(int x, int base)
{
return (int) (Math.log(x)/Math.log(base));
}
esto imprime:
error at 3^5
error at 3^10
error at 3^13
error at 3^15
error at 3^17
error at 9^5
error at 10^3
error at 10^6
error at 10^9
error at 11^7
error at 12^7
...
Para eliminar completamente los errores, tuve que agregar épsilon, que está entre 1e-11 y 1e-14. ¿Podría haber dicho esto antes de las pruebas? Definitivamente no pude.
¿Cómo probar el rendimiento de este ? En mi sistema (Core i7, jdk 1.6 x64), la versión entera es casi 10 veces más rápida que la versión de coma flotante. ¡Asegúrese de hacer algo con el resultado de la función para que el JIT no pueda eliminar el cálculo por completo! – x4u
Tiene razón. No usé los resultados del cálculo y el compilador ha optimizado algo. Ahora tengo el mismo resultado que usted: la función entera es 10 veces más rápida (Core 2 Duo, jdk 1.6 c64) – Nulldevice
Esto le da efectivamente 'Math.floor (Math.log (n) /Math.log (2))', así que no es realmente calcular base de registro 2! – Dori