La aritmética de punto flotante no produce resultados exactos [duplicate]

7 minutos de lectura

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 salida por el 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 puedes confiar en los resultados de la aritmética realizada con ellos? ¿Cómo puedo solucionar este problema?

  • Bienvenido a la informática. 🙂

    – BobbyShaftoe

    2 de noviembre de 2009 a las 13:25

  • Vea esta pregunta, que aunque está formulada de manera diferente, da la misma respuesta. stackoverflow.com/questions/1088216/…

    – Yishai

    2 de noviembre de 2009 a las 14:09

  • La pregunta puede reformularse como: Aritmética inexacta que no produce valores exactos. ¡Apuesta!

    – Ingo

    2 de febrero de 2013 a las 12:12

  • no es por ser injusto, pero los programadores deberían leer un poco sobre la representación de tipos, como en todos los cursos serios de Ciencias de la Computación. en Mi universidad, el primer examen se trata de calcular manualmente los números IEEE… 🙂

    – ingconti

    16 de agosto de 2017 a las 10:10


La aritmetica de punto flotante no produce resultados exactos duplicate
jon skeet

Si necesitas exacto decimal valores, debe utilizar java.math.BigDecimal. Entonces lee “Lo que todo informático debe saber sobre la aritmética de punto flotante” para conocer los antecedentes de por qué está obteniendo esos resultados.

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

  • Tenga en cuenta, BigDecimal va a ser mucho más lento que la simple aritmética de punto flotante.

    –Anthony Mills

    2 de noviembre de 2009 a las 13:27

  • @Anthony: Cierto, pero si realmente necesitar valores decimales exactos, entonces lento y correcto es mejor que rápido e incorrecto.

    – Jon Skeet

    2 de noviembre de 2009 a las 13:47

  • También puede usar números enteros para centavos en este caso. Tendrá que recordar que 218 significa 2,18 y necesita hacer un trabajo extra para imprimir.

    – estrella azul

    2 de noviembre de 2009 a las 14:35

  • Me encantan las matemáticas, e incluso yo no puedo terminar ese artículo. Desearía que la gente dejara de publicarlo; no es una buena referencia, a menos que desee leer un pequeño libro solo para comprender cómo funcionan los números de coma flotante.

    – Blue Raja – Danny Pflughoeft

    08/04/2011 a las 19:52

  • flotante-punto-gui.de tiene un enfoque un poco más simple y más práctico para explicar los problemas.

    – Joaquín Sauer

    20 de marzo de 2013 a las 13:21

Los números de coma flotante usan fracciones binarias y no fracciones decimales. Es decir, estás acostumbrado a las 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, entonces tienen un 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 solo puede almacenar un número finito de dígitos, en algún momento esto tiene que cortarse, 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 necesita fracciones decimales exactas, use BigDecimal. Si el rendimiento es un problema, puede lanzar sus propias fracciones decimales. Por ejemplo, si sabe que siempre quiere exactamente 3 lugares decimales y que los números no serán más de un millón, simplemente puede usar int con 3 lugares decimales supuestos, haciendo los ajustes necesarios cuando hace aritmética y escribe 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.

Los números de coma flotante son imprecisos, especialmente porque funcionan en fracciones binarias (1/2, 1/4, 1/8, 1/16, 1/32, …) en lugar de fracciones decimales (1/10, 1/ 100, 1/1000, …). Simplemente defina lo que siente que es “lo suficientemente cerca” y use algo como Math.abs(a-b) < 0.000001.

La aritmetica de punto flotante no produce resultados exactos duplicate
Arrendajo

En una nota filosófica, me pregunto: la mayoría de las CPU de las computadoras de hoy tienen soporte incorporado para la aritmética de enteros y la aritmética de punto flotante, pero no para la aritmética decimal. ¿Por qué no? No he escrito una aplicación en años donde los flotadores fueran utilizables debido a este problema de redondeo. Ciertamente no puede usarlos para cantidades de dinero: nadie quiere imprimir un precio en un recibo de venta de “$42.3200003”. Ningún contador va a aceptar “podríamos estar equivocados por un centavo aquí y allá porque estamos usando fracciones binarias y tuvimos errores de redondeo”.

Los flotadores están bien para mediciones, como la distancia o la temperatura, donde no existe una “respuesta exacta” y, de todos modos, debe redondear la precisión de sus instrumentos en algún momento. Supongo que para las personas que están programando la computadora en el laboratorio de química, los flotadores se usan de manera rutinaria. Pero para aquellos de nosotros en el mundo de los negocios, son prácticamente inútiles.

En aquellos días antiguos cuando programaba en mainframes, la familia de CPU IBM 360 tenía soporte integrado para la aritmética decimal empaquetada. Almacenaron cadenas donde cada byte contenía dos dígitos decimales, es decir, los primeros cuatro bits tenían valores de 0 a 9 y lo mismo los segundos cuatro bits, y la CPU tenía funciones aritméticas para manipularlos. ¿Por qué Intel no puede hacer algo así? Entonces Java podría agregar un tipo de datos “decimal” y no necesitaríamos toda la basura adicional.

No estoy diciendo que abolimos las carrozas, por supuesto. Solo agrega decimales.

Bueno, como van los grandes movimientos sociales, no creo que este vaya a generar mucha agitación popular o disturbios en las calles.

Puede hacer que la salida de su programa se parezca más a lo que espera utilizando una salida formateada.

http://java.sun.com/javase/6/docs/api/java/util/Formatter.html

Obviamente, la aritmética de punto flotante subyacente todavía funciona igual, pero al menos la salida será más legible.

Por ejemplo, para redondear los resultados a dos decimales:

System.out.print(String.format(".2f", i) + ","); 

La aritmetica de punto flotante no produce resultados exactos duplicate
don_q

Puede escribir algún código para calcular Epsilon en su máquina. Creo que std:: C ++ lo define, se define de otras maneras según lo que esté usando.

private static float calcEpsilonFloat() {
    float epsi = 1.0f;


    while ((float) (1.0 + (epsi / 2.0)) != 1.0)
    {
       epsi /= 2.0f;
    }

    return epsi;
}

La única vez que me preocupé por Epsilon fue cuando estaba comparando señales para el umbral. Incluso entonces, no estoy seguro de si realmente necesitaba preocuparme por eso, según mi experiencia, si está preocupado por Epsilon puedes tener alguna otra consideración con la que tratar primero.

1646970321 55 La aritmetica de punto flotante no produce resultados exactos duplicate
cr0

Por cierto, puede intentar usar esta función para asegurarse (para no tener demasiados dígitos decimales) de que su número será reformateado para mantener solo los decimales que necesita.

http://pastebin.com/CACER0xK

n es su número con muchos decimales (por ejemplo Math.PI),
numberOfDecimals es el número máximo de decimales que necesita (por ejemplo, 2 para 3,14 o 3 para 3,151).

Por teoría, poner un valor negativo a numberOfDecmals, también cortará los dígitos enteros inferiores del número. por ejemplo, poner n=1588.22 y numberOfDecimals=-2la función volverá 1500.0.

Avísame si hay algo mal.

  • No funciona, y el código debería haber sido publicado aquí.

    – usuario207421

    27 de enero de 2015 a las 3:38

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad