2010-07-12 22 views
11

¿Cómo lidiar con el comportamiento extraño de Java con el operador de módulo al usar dobles?Módulo con dobles en Java

Por ejemplo, se podría esperar que el resultado de 3.9 - (3.9 % 0.1) sea 3.9 (y, de hecho, Google dice que no voy loco), pero cuando lo ejecuto en Java me sale 3.8000000000000003.

Entiendo que esto es el resultado de cómo se duplican las tiendas y los procesos de Java, pero ¿hay alguna manera de evitarlo?

+1

¿Qué estás tratando de hacer? Tengo la sensación de que realmente no necesitas '%' en absoluto. – polygenelubricants

Respuesta

15

utilizar un tipo preciso si se necesita un resultado preciso:

double val = 3.9 - (3.9 % 0.1); 
    System.out.println(val); // 3.8000000000000003 

    BigDecimal x = new BigDecimal("3.9"); 
    BigDecimal bdVal = x.subtract(x.remainder(new BigDecimal("0.1"))); 
    System.out.println(bdVal); // 3.9 

Por qué 3,8000 ... 003? Porque Java usa la FPU para calcular el resultado. 3.9 es imposible de almacenar exactamente en notación de doble precisión IEEE, por lo que almacena 3.89999 ... en su lugar. Y 3.8999% 0.01 da 0.09999 ... por lo tanto, el resultado es un poco mayor que 3.8.

+0

+1 Odiaba trabajar con números flotantes IEEE a mano en mi clase de ingeniería eléctrica, pero valió la pena. –

+0

Gracias, te irá bien. – Andy

+0

Buena respuesta. Entonces, ¿por qué la FPU puede deducir que '((3.9D/0.1D)% 1D) = 0.0' (básicamente el mismo problema ligeramente reorganizado) cuando debido al error de redondeo piensa '(3.9D% 0.1D) = 0.0999999999999997'? –

1

Si conoce la cantidad de decimales con los que está tratando, intente convertir primero a enteros. Este es solo un caso clásico de inexactitud en coma flotante. En vez de hacer 3.9 % 0.1 que estás haciendo algo así como 3.899 % 0.0999

+1

Y Google alegará que 3.9% 0.1 = -4.4408921 × 10-16 que tampoco encaja con el módulo. – Stroboskop

2

De The Java Language Specification:

El resultado de una operación de resto de punto flotante calculado por el operador % no es la misma que la producido por la operación resto definido por IEEE 754. El IEEE 754 resto de la operación calcula el resto de una división de redondeo, no es una división truncada, por lo que su comportamiento no es análogo al de el operador de resto entero habitual. En su lugar, el lenguaje de programación Java define% en las operaciones de coma flotante para comportarse de forma análoga a que del operador del resto entero; esto se puede comparar con la función de biblioteca C fmod. La operación de resto de IEEE 754 puede ser calculada por la rutina de la biblioteca Math.IEEEremainder.

En otras palabras, esto se debe al hecho de que Java redondea el resultado de la división involucrada en el cálculo del resto, mientras que IEEE754 especifica truncar la respuesta de la división. Este caso particular parece exponer esta diferencia muy claramente.

Usted puede obtener la respuesta que usted espera utilizar Math.IEEEremainder:

System.out.println(3.9 - (3.9 % 0.1)); 
System.out.println(3.9 - Math.IEEEremainder(3.9, 0.1));