Esto es lo que está sucediendo en binario. Como sabemos, algunos valores de punto flotante no se pueden representar exactamente en binario, incluso si se pueden representar exactamente en decimal. Estos 3 números son solo ejemplos de ese hecho.
Con este programa, saco las representaciones hexadecimales de cada número y los resultados de cada suma.
public class Main{
public static void main(String args[]) {
double x = 23.53; // Inexact representation
double y = 5.88; // Inexact representation
double z = 17.64; // Inexact representation
double s = 47.05; // What math tells us the sum should be; still inexact
printValueAndInHex(x);
printValueAndInHex(y);
printValueAndInHex(z);
printValueAndInHex(s);
System.out.println("--------");
double t1 = x + y;
printValueAndInHex(t1);
t1 = t1 + z;
printValueAndInHex(t1);
System.out.println("--------");
double t2 = x + z;
printValueAndInHex(t2);
t2 = t2 + y;
printValueAndInHex(t2);
}
private static void printValueAndInHex(double d)
{
System.out.println(Long.toHexString(Double.doubleToLongBits(d)) + ": " + d);
}
}
El printValueAndInHex
método es solo un ayudante de impresora hexadecimal.
El resultado es el siguiente:
403787ae147ae148: 23.53
4017851eb851eb85: 5.88
4031a3d70a3d70a4: 17.64
4047866666666666: 47.05
--------
403d68f5c28f5c29: 29.41
4047866666666666: 47.05
--------
404495c28f5c28f6: 41.17
4047866666666667: 47.050000000000004
Los primeros 4 números son x
, y
, z
, y s
's representaciones hexadecimales. En la representación de coma flotante IEEE, los bits 2-12 representan el exponente binario , es decir, la escala del número. (El primer bit es el bit de signo y los bits restantes para la mantisa .) El exponente representado es en realidad el número binario menos 1023.
Se extraen los exponentes para los primeros 4 números:
sign|exponent
403 => 0|100 0000 0011| => 1027 - 1023 = 4
401 => 0|100 0000 0001| => 1025 - 1023 = 2
403 => 0|100 0000 0011| => 1027 - 1023 = 4
404 => 0|100 0000 0100| => 1028 - 1023 = 5
Primer conjunto de adiciones
El segundo número ( y
) es de menor magnitud. Al sumar estos dos números para obtener x + y
, los últimos 2 bits del segundo número ( 01
) se desplazan fuera del rango y no figuran en el cálculo.
La segunda suma agrega x + y
y z
agrega dos números de la misma escala.
Segundo conjunto de adiciones
Aquí, x + z
ocurre primero. Son de la misma escala, pero producen un número que es más alto en la escala:
404 => 0|100 0000 0100| => 1028 - 1023 = 5
La segunda suma agrega x + z
y y
, y ahora se eliminan 3 bits y
para agregar los números ( 101
). Aquí, debe haber una ronda hacia arriba, porque el resultado es el siguiente número de coma flotante hacia arriba: 4047866666666666
para el primer conjunto de adiciones vs.4047866666666667
para el segundo conjunto de adiciones. Ese error es lo suficientemente significativo como para mostrarse en la impresión del total.
En conclusión, tenga cuidado al realizar operaciones matemáticas en números IEEE. Algunas representaciones son inexactas, y se vuelven aún más inexactas cuando las escalas son diferentes. Suma y resta números de escala similar si puedes.
(2.0^53 + 1) - 1 == 2.0^53 - 1 != 2^53 == 2^53 + (1 - 1)
. Ej .). Por lo tanto, sí: tenga cuidado al elegir el orden de las sumas y otras operaciones. Algunos lenguajes incorporan funciones para realizar sumas de "alta precisión" (p. Ej., Pythonmath.fsum
), por lo que puede considerar utilizar estas funciones en lugar del algoritmo de suma ingenua.