La aritmética de coma flotante no produce resultados exactos [duplicado]

Resuelto asked hace 15 años • 7 respuestas

Necesito hacer algo de aritmética de punto flotante en Java como se muestra en el siguiente código:

public class TestMain {
    private static Map<Integer, Double> ccc = new HashMap<Integer, Double>() {
      { put(1, 0.01); put(2, 0.02); put(3, 0.05); put(4, 0.1); put(6, 0.2);
        put(10, 0.5); put(20, 1.0); put(30, 2.0); put(50, 5.0); put(100, 10.0);
      }
    };

    Double increment(Double i, boolean up) {
        Double inc = null;

        while (inc == null) {
            inc = ccc.get(i.intValue());

            if (up)
                --i;
            else
                ++i;
        }
        return inc;
    }

    public static void main(String[] args) {
        TestMain tt = new TestMain();

        for (double i = 1; i < 1000; i += tt.increment(i, true)) {
            System.out.print(i + ",");
        }
    }
}

Esto es para simular el rango de valores dados como resultado del widget giratorio de Betfair .

La aritmética de punto flotante en Java parece introducir algunos errores inesperados. Por ejemplo, obtengo 2,180000000000001 en lugar de 2,18. ¿De qué sirven los números de punto flotante si no se puede confiar en los resultados de la aritmética realizada sobre ellos? ¿Cómo puedo solucionar este problema?

 avatar Nov 02 '09 20:11
Aceptado

Si necesita valores decimales exactos , debe utilizar java.math.BigDecimal. Luego lea "Lo que todo informático debe saber sobre la aritmética de punto flotante" para conocer los motivos por los que obtiene esos resultados.

(Tengo un artículo centrado en .NET que puede resultarle más fácil de leer, y ciertamente más breve. Las diferencias entre Java y .NET son en su mayoría irrelevantes para comprender este problema).

Jon Skeet avatar Nov 02 '2009 13:11 Jon Skeet

Los números de coma flotante utilizan fracciones binarias y no fracciones decimales. Es decir, estás acostumbrado a fracciones decimales formadas por un dígito de décimas, un dígito de centésimas, un dígito de milésimas, etc. d1/10 + d2/100 + d3/1000... Pero los números de coma flotante están en binario, por lo que tienen medio dígito, un cuarto de dígito, un octavo dígito, etc. d1/2 + d2/4 + d3/8...

Muchas fracciones decimales no se pueden expresar exactamente en un número finito de dígitos binarios. Por ejemplo, 1/2 no es problema: en decimal es .5, en binario es .1. 3/4 es decimal .75, binario .11. Pero 1/10 es un .1 limpio en decimal, pero en binario es .0001100110011... con el "0011" repitiéndose para siempre. Como la computadora sólo puede almacenar un número finito de dígitos, en algún momento este tiene que ser cortado, por lo que la respuesta no es precisa. Cuando volvemos a convertir a decimal en la salida, obtenemos un número de aspecto extraño.

Como dice Jon Skeet, si necesitas fracciones decimales exactas, usa BigDecimal. Si el rendimiento es un problema, puedes calcular tus propias fracciones decimales. Por ejemplo, si sabes que siempre quieres exactamente 3 decimales y que los números no serán más de un millón aproximadamente, puedes simplemente usar int con 3 decimales asumidos, haciendo los ajustes necesarios cuando haces aritmética y escribes una salida. Función de formato para insertar el punto decimal en el lugar correcto. Pero el 99% de las veces el rendimiento no es un problema lo suficientemente grande como para que valga la pena.

Jay avatar Nov 02 '2009 14:11 Jay