java - interfaz - ¿Por qué agregar 0.1 múltiples veces permanece sin pérdidas?
java swing tutorial pdf español (3)
Sé que el número decimal
0.1
no se puede representar exactamente con un número binario finito (
explanation
), por lo que el
double n = 0.1
perderá algo de precisión y no será exactamente
0.1
.
Por otro lado,
0.5
se puede representar exactamente porque es
0.5 = 1/2 = 0.1b
.
Una vez dicho esto, es comprensible que agregar
0.1
tres veces
no dé exactamente
0.3
por lo que el siguiente código se imprime
false
:
double sum = 0, d = 0.1;
for (int i = 0; i < 3; i++)
sum += d;
System.out.println(sum == 0.3); // Prints false, OK
Pero entonces, ¿cómo es que sumar
0.1
cinco veces
dará exactamente
0.5
?
El siguiente código se imprime
true
:
double sum = 0, d = 0.1;
for (int i = 0; i < 5; i++)
sum += d;
System.out.println(sum == 0.5); // Prints true, WHY?
Si
0.1
no se puede representar exactamente, ¿cómo es que sumarlo 5 veces da exactamente
0.5
que se puede representar con precisión?
El error de redondeo no es aleatorio y la forma en que se implementa intenta minimizar el error. Esto significa que a veces el error no es visible o que no hay error.
Por ejemplo,
0.1
no es exactamente
0.1
es decir,
new BigDecimal("0.1") < new BigDecimal(0.1)
pero
0.5
es exactamente
1.0/2
Este programa le muestra los verdaderos valores involucrados.
BigDecimal _0_1 = new BigDecimal(0.1);
BigDecimal x = _0_1;
for(int i = 1; i <= 10; i ++) {
System.out.println(i+" x 0.1 is "+x+", as double "+x.doubleValue());
x = x.add(_0_1);
}
huellas dactilares
0.1000000000000000055511151231257827021181583404541015625, as double 0.1
0.2000000000000000111022302462515654042363166809082031250, as double 0.2
0.3000000000000000166533453693773481063544750213623046875, as double 0.30000000000000004
0.4000000000000000222044604925031308084726333618164062500, as double 0.4
0.5000000000000000277555756156289135105907917022705078125, as double 0.5
0.6000000000000000333066907387546962127089500427246093750, as double 0.6000000000000001
0.7000000000000000388578058618804789148271083831787109375, as double 0.7000000000000001
0.8000000000000000444089209850062616169452667236328125000, as double 0.8
0.9000000000000000499600361081320443190634250640869140625, as double 0.9
1.0000000000000000555111512312578270211815834045410156250, as double 1.0
Nota: que
0.3
está ligeramente apagado, pero cuando llegas a
0.4
los bits tienen que cambiar uno hacia abajo para ajustarse al límite de 53 bits y el error se descarta.
Nuevamente, un error regresa para
0.6
y
0.7
pero para
0.8
a
1.0
el error se descarta.
Agregarlo 5 veces debería acumular el error, no cancelarlo.
La razón por la que hay un error se debe a la precisión limitada.
es decir, 53 bits.
Esto significa que a medida que el número usa más bits a medida que se hace más grande, los bits deben eliminarse al final.
Esto provoca redondeo que en este caso está a su favor.
Puede obtener el efecto contrario cuando obtiene un número menor, por ejemplo,
0.1-0.0999
=>
1.0000000000000286E-4
y ve más errores que antes.
Un ejemplo de esto es por qué en Java 6 ¿Por qué Math.round (0.49999999999999994) devuelve 1? En este caso, la pérdida de un bit en el cálculo resulta en una gran diferencia en la respuesta.
Los sistemas de punto flotante hacen varias magias, incluyendo tener algunos bits adicionales de precisión para redondear. Por lo tanto, el error muy pequeño debido a la representación inexacta de 0.1 termina siendo redondeado a 0.5.
Piense en el punto flotante como una excelente pero INEXACTA forma de representar números. No todos los números posibles se representan fácilmente en una computadora. Números irracionales como PI. O como SQRT (2). (Los sistemas matemáticos simbólicos pueden representarlos, pero dije "fácilmente").
El valor de coma flotante puede ser extremadamente cercano, pero no exacto. Puede estar tan cerca que podría navegar a Plutón y estar en milímetros. Pero aún no es exacto en un sentido matemático.
No use coma flotante cuando necesite ser exacto en lugar de aproximado. Por ejemplo, las aplicaciones de contabilidad desean realizar un seguimiento exacto de un cierto número de centavos en una cuenta. Los enteros son buenos para eso porque son exactos. El problema principal que debe tener en cuenta con los enteros es el desbordamiento.
Usar BigDecimal para la moneda funciona bien porque la representación subyacente es un número entero, aunque grande.
Reconociendo que los números de coma flotante son inexactos, todavía tienen muchos usos. Sistemas de coordenadas para navegación o coordenadas en sistemas gráficos. Valores astronómicos. Valores científicos (De todos modos, es probable que no se pueda conocer la masa exacta de una pelota de béisbol dentro de una masa de un electrón, por lo que la inexactitud realmente no importa).
Para contar aplicaciones (incluida la contabilidad) use integer. Para contar la cantidad de personas que pasan por una puerta, use int o long.
Salvo desbordamiento, en coma flotante,
x + x + x
es exactamente el número de coma flotante correctamente redondeado (es decir, el más cercano) al real 3 *
x
,
x + x + x + x
es exactamente 4 *
x
, y
x + x + x + x + x
es nuevamente la aproximación de punto flotante correctamente redondeada para 5 *
x
.
El primer resultado, para
x + x + x
, deriva del hecho de que
x + x
es exacto.
x + x + x
es, por lo tanto, el resultado de un solo redondeo.
El segundo resultado es más difícil,
here
se analiza una demostración (y Stephen Canon alude a otra prueba por análisis de caso en los últimos 3 dígitos de
x
).
Para resumir, 3 *
x
está en la misma
binade
que 2 *
x
o está en la misma binada que 4 *
x
, y en cada caso es posible deducir que el error en la tercera suma cancela el error en la segunda Además (la primera adición es exacta, como ya dijimos).
El tercer resultado, "
x + x + x + x + x
está correctamente redondeado", deriva del segundo de la misma manera que el primero deriva de la exactitud de
x + x
.
El segundo resultado explica por qué
0.1 + 0.1 + 0.1 + 0.1
es exactamente el número de coma flotante
0.4
: los números racionales 1/10 y 4/10 se aproximan de la misma manera, con el mismo error relativo, cuando se convierten en coma flotante.
Estos números de coma flotante tienen una relación de exactamente 4 entre ellos.
El primer y el tercer resultado muestran que
0.1 + 0.1 + 0.1
y
0.1 + 0.1 + 0.1 + 0.1 + 0.1
pueden tener menos errores de los que pueden deducirse del análisis ingenuo de errores, pero, en sí mismos, solo relacionan los resultados con respectivamente
3 * 0.1
y
5 * 0.1
, que se puede esperar que estén cerca pero no necesariamente idénticos a
0.3
y
0.5
.
Si sigue sumando
0.1
después de la cuarta adición, finalmente observará errores de redondeo que hacen que "
0.1
agregado a sí mismo n veces" diverja de
n * 0.1
, y diverja aún más de n / 10.
Si tuviera que trazar los valores de "0.1 agregado a sí mismo n veces" en función de n, observaría líneas de pendiente constante por binadas (tan pronto como el resultado de la enésima suma esté destinado a caer en una binada particular, Se puede esperar que las propiedades de la adición sean similares a las adiciones anteriores que produjeron un resultado en la misma binada).
Dentro de una misma binada, el error crecerá o disminuirá.
Si observara la secuencia de las pendientes de binade a binade, reconocería los dígitos repetidos de
0.1
en binario durante un tiempo.
Después de eso, la absorción comenzaría a tener lugar y la curva se volvería plana.