Java: Эпсилон с двумя машинами - это не самый маленький x такой, что 1 + x! = 1?
Я пытаюсь определитьdouble
машина эпсилон в Java, используя определение его как наименьшего представимогоdouble
значениеx
такой, что1.0 + x != 1.0
так же, как в C / C ++. Согласно википедии, эта машина эпсилон равна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
Как мы видим, у нас есть эпсилон меньше, чем машина, который, добавленный к 1, дает не 1, что противоречит определению.
Так что же не так с общепринятым значением для машины эпсилон согласно этому определению? Или я что-то пропустил? Я подозреваю другой эзотерический аспект математики с плавающей точкой, но я не вижу, где я ошибся ...
РЕДАКТИРОВАТЬ: Спасибо комментаторам, я наконец-то понял. Я на самом деле использовал неправильное определение!eps = Math.ulp(1.0)
вычисляет расстояние до наименьшего представимого двойника>1.0
, но - и в этом все дело - чтоeps
являетсяне наименьшийx
с1.0 + x != 1.0
, а скорее одважды это значение: добавление1.0 + Math.nextAfter(eps/2)
округляетсявверх в1.0 + eps
.