Java: la máquina doble épsilon no es la x más pequeña, de modo que 1 + x! = 1?
Estoy tratando de determinar eldouble
máquina épsilon en Java, usando la definición de ser el más pequeño representabledouble
valorx
tal que1.0 + x != 1.0
, al igual que en C / C ++. Según wikipedia, esta máquina épsilon es igual a2^-52
(con 52 siendo el número dedouble
bits de mantisa - 1).
Mi implementación usa elMath.ulp()
función:
double eps = Math.ulp(1.0);
System.out.println("eps = " + eps);
System.out.println("eps == 2^-52? " + (eps == Math.pow(2, -52)));
y los resultados son lo que esperaba:
eps = 2.220446049250313E-16
eps == 2^-52? true
Hasta aquí todo bien. Sin embargo, si verifico que el dadoeps
es de hecho elpequeñísimo x
tal que1.0 + x != 1.0
, parece haber uno más pequeño, también conocido comoanterior double
valor de acuerdo aMath.nextAfter()
:
double epsPred = Math.nextAfter(eps, Double.NEGATIVE_INFINITY);
System.out.println("epsPred = " + epsPred);
System.out.println("epsPred < eps? " + (epsPred < eps));
System.out.println("1.0 + epsPred == 1.0? " + (1.0 + epsPred == 1.0));
Cuyos rendimientos:
epsPred = 2.2204460492503128E-16
epsPred < eps? true
1.0 + epsPred == 1.0? false
Como vemos, tenemos un épsilon más pequeño que la máquina, que, sumado a 1, no produce 1, en contradicción con la definición.
Entonces, ¿qué tiene de malo el valor comúnmente aceptado para la máquina épsilon según esta definición? ¿O me perdí algo? Sospecho otro aspecto esotérico de las matemáticas de punto flotante, pero no puedo ver dónde me equivoqué ...
EDITAR: Gracias a los comentaristas, finalmente lo entendí. ¡En realidad usé la definición incorrecta!eps = Math.ulp(1.0)
calcula la distancia al doble representable más pequeño>1.0
, pero, y ese es el punto, queeps
esno el mas pequeñox
con1.0 + x != 1.0
, sino más bien sobredos veces ese valor: Agregar1.0 + Math.nextAfter(eps/2)
es redondeadoarriba a1.0 + eps
.