Я пытаюсь определить double
машинный epsilon в Java, используя определение его как наименьшее представляемое значение double
x
, такое, что 1.0 + x != 1.0
, как и в C/С++. Согласно wikipedia, этот epsilon машины равен 2^-52
(при 52 - количество бит double
мантисса - 1).
В моей реализации используется функция Math.ulp()
:
double eps = Math.ulp(1.0);
System.out.println("eps = " + eps);
System.out.println("eps == 2^-52? " + (eps == Math.pow(2, -52)));
и ожидаемые результаты:
eps = 2.220446049250313E-16
eps == 2^-52? true
До сих пор так хорошо. Однако, если я проверю, что данный eps
действительно наименьший x
такой, что 1.0 + x != 1.0
, кажется, что он меньше, например предыдущее значение double
в соответствии с Math.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));
Что дает:
epsPred = 2.2204460492503128E-16
epsPred < eps? true
1.0 + epsPred == 1.0? false
Как мы видим, мы имеем меньше машинного epsilon, который, добавленный к 1, дает не 1, что противоречит определению.
Итак, что не так с общепринятым значением для машинного epsilon в соответствии с этим определением? Или я что-то пропустил? Я подозреваю еще один эзотерический аспект математики с плавающей запятой, но я не вижу, где я ошибся...
EDIT: Спасибо комментаторам, я наконец получил его. Я использовал неправильное определение! eps = Math.ulp(1.0)
вычисляет расстояние до наименьшего представляемого двойникa > 1.0
, но - и что точка - это eps
не самая маленькая x
с 1.0 + x != 1.0
, а примерно вдвое больше: Добавление 1.0 + Math.nextAfter(eps/2)
округляется до 1.0 + eps
.