2011-10-11 13 views
36

Al multiplicar un número de coma flotante que está muy cerca de 1 con un int> 0, puede jamás interpretarse como 1.¿Puede 0.99999999999 redondearse a 1.0 al multiplicar?

Es decir, si Math.random() devuelve su resultado más alto posible (que es 1 paso por debajo de 1,0) , será

(int)(Math.random() * 8) 

ser 8 o 7?

Para un ejemplo práctico, esta construcción puede uso frecuente dar un índice de error de límites:

someArray[(int)(Math.random() * someArray.length)]; 

Estoy especialmente interesado en las respuestas de Java y ActionScript 3, pero supongo que todos ellos utilizan la las mismas reglas para la aritmética de coma flotante, y las respuestas para cualquier plataforma serían útiles.

Actualización: A pesar de que ya se aceptó una respuesta, yo todavía aprecio confirmación de que esto no puede ir mal en ActionScript 3 tampoco, ya que un colega informando que lo vio salir mal una vez que es lo que me impulsó a parte haz esta pregunta

+0

Estaría muy sorprendido si un número tan * * Nunca se desbordó en la siguiente int ... pero voy a esperar a una mejor respuesta ... – bdares

+4

@UdoFholl No tenía la intención de utilizar ninguna notación matemática oficial, solo quería indicar un número con muchas nueves. –

+0

* Al multiplicar un número flotante que está muy cerca de 1, ¿puede interpretarse alguna vez como 1 *? Sí, si lo multiplicas por 0 ;-) JK – aioobe

Respuesta

44

Si multiplica el valor mayor por debajo de 1.0 con someInt (> 0), el resultado nunca será someInt.

Esto se puede probar exhaustivamente para enteros como esto:

Double greatestLessThanOne = Double.longBitsToDouble(4607182418800017407L); 

// Assert that greatestLessThanOne is indeed the largest double less than 1. 
//assert 1.0 == greatestLessThanOne + Math.ulp(greatestLessThanOne); 

for (int i = 1; i >= 0; i++) 
    if ((int) (greatestLessThanOne * i) == i) 
     System.out.println("Exception found: " + i); 

El fragmento no produce ninguna salida.

(Math.ulp devuelve la distancia entre el dado doble y el valor doble siguiente más grande en magnitud. Por tanto, la afirmación asegura que greatestLessThanOne es de hecho el mayor valor de menos de 1,0.)

En otras palabras, su línea

Object element = elementArray[(int)(Math.random() * elementArray.length)]; 

nunca dará lugar a una ArrayIndexOutOfBoundsException.


Por otra parte, de acuerdo con Mark Dickinsons comentario sobre here, esto es válido también cuando se multiplican con un doble.

With IEEE 754 floating-point arithmetic in round-to-nearest mode, you can show that x * y < y for any x < 1.0 and any non-tiny positive y . (It can fail if y is either subnormal or the smallest positive normal number.)

+2

Buena respuesta. Y [Java] (http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html) utiliza round-to-nearest: "El lenguaje de programación Java requiere que la aritmética de coma flotante se comporte como si cada operador de punto flotante redondea su resultado de punto flotante a la precisión del resultado. Los resultados inexactos deben redondearse al valor representable más cercano al resultado infinitamente preciso, si los dos valores representables más cercanos son igualmente cercanos, el que tiene su bit cero menos significativo se elige. Este es el modo de redondeo predeterminado del estándar IEEE 754 conocido como ** redondear al más cercano **. " –

+1

gracias por la excelente lección de Java +1 – mKorbel

+0

Se aceptó esta respuesta ya que la mayoría de las plataformas seguirán a IEEE 754. Aún así, ¿sabe si ActionScript 3/Flash también lo hace? Escuché rumores de que realmente puede salir mal allí. –

-2

a la vuelta de él, puede ser así:

BigDecimal bd = new BigDecimal(Double.toString(d)); 
bd = bd.setScale(decimalPlace,BigDecimal.ROUND_HALF_UP); 
+10

' BigDecimal'? Esa es una "solución" bastante costosa (si de hecho es necesaria) para una declaración simple que se puede ejecutar con demasiada frecuencia para eso. –

+1

Esto no es solo costoso, también es un problema para las características estadísticas de la distribución aleatoria. – leftaroundabout

+0

no, me refiero a solo ejemplo, puede usar otro tipo de objeto más pequeño que bigdecimal –