Подтвердить что ты не робот

Java: Двойная машина epsilon не является наименьшей x такой, что 1 + x!= 1?

Я пытаюсь определить 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.

4b9b3361

Ответ 1

используя определение того, что это наименьшее представляемое двойное значение x такое, что 1.0 + x!= 1.0, как и в C/С++

Это никогда не было определением, а не на Java, а не на C, а не на С++.

Определение состоит в том, что epsilon машины - это расстояние между одним и самым маленьким поплавком/двойным размером больше единицы.

Ваше "определение" неверно почти в 2 раза.

Кроме того, отсутствие strictfp допускает больший диапазон экспоненциальности и не должно оказывать никакого влияния на эмпирическое измерение epsilon, поскольку оно вычисляется из 1.0 и его преемника, каждый из которых и разность которых может быть представлен стандартным диапазоном экспоненты.

Ответ 2

Я не уверен, что ваш экспериментальный метод/теория звучит. Документация для класса Math:

Для заданного формата с плавающей запятой ulp определенного значения действительного числа - это расстояние между двумя скользящими значениями с плавающей запятой, что числовое значение

В документации для метода ulp указано:

Значение ulp двойного значения - это положительное расстояние между этим значением с плавающей запятой и двойным значением, которое больше по величине

Итак, если вы хотите наименьшее значение eps, такое, что 1.0 + eps != 1.0, ваш eps действительно должен быть меньше Math.ulp(1.0), так как по крайней мере для любого значения, большего чем Math.ulp(1.0) / 2, результат будет округлен.

Я думаю, что наименьшее такое значение будет дано Math.nextAfter(eps/2, 1.0).