Почему "(2.5 <2.5 + Number.EPSILON)" ложно в JavaScript? - PullRequest
10 голосов
/ 04 июня 2019

Я хочу найти значения меньше определенного значения в массиве. Я попытался использовать Number.EPSILON, потому что входное значение не является определенным значением (например, 1.5000000000001).

Я нашел что-то странное во время теста:

>> (1.5 < 1.5 + Number.EPSILON) 
<- true 
>> (2.5 < 2.5 + Number.EPSILON)
<- false

Почему это? Средой тестирования является консоль браузера Chrome.

Ответы [ 3 ]

6 голосов
/ 04 июня 2019

Хотя Number.EPSILON само по себе может быть точно представлено, это не гарантирует, что добавление значений к нему (или дальнейшее манипулирование им) приведет к идеальной точности.В этом случае 1.5 + Number.EPSILON приводит к значению, немного превышающему 1,5:

console.log(1.5 + Number.EPSILON);

Что явно больше 1,5.С другой стороны, при добавлении 2,5 к Number.EPSILON получается ровно 2,5 - точность, на которую вы надеялись, была потеряна в процессе сложения.

console.log(2.5 + Number.EPSILON);

2.5 < 2.5 оценивается как false, как и ожидалось.

1 голос
/ 04 июня 2019

Числа с плавающей точкой имеют ограниченную точность. В зависимости от языка и архитектуры они обычно представляются с использованием 32 бит (float) или 64 бит (double, с «двойной точностью»). Хотя в нетипизированном языке, таком как JavaScript, все становится размытым, под всем этим все еще есть реальная машина, и эта машина должна выполнять арифметику с плавающей запятой.

Проблема в том, что результаты некоторых вычислений не могут быть представлены точно, учитывая ограниченную точность. Это объясняется некоторыми примерами на странице Википедии об арифметике с плавающей запятой .

Для людей, которым нужны все мелкие детали, обычно рекомендуется статья о Что каждый компьютерный специалист должен знать об арифметике с плавающей точкой . А если серьезно: не каждый компьютерщик должен знать все это, и я уверен, что только немногие люди в мире на самом деле прочитали все это ...

В качестве чрезмерно наводящего примера: представьте, что у вас есть 5 цифр для хранения номера. Когда у вас есть дополнение, например

  10000.
+     0.00001
--------------------
= 10000.

часть .00001 будет в основном "усечена", поскольку она не вписывается в 5 цифр.

(это не совсем то, как это работает, но следует изложить идею)

Фактическое значение для Number.EPSILON, согласно документации , составляет приблизительно 2,22 * 10-16 и представляет собой разницу "между 1 и наименьшее число с плавающей запятой больше 1 ". (Это иногда называют ULP, Unit In The Last Place ).

Таким образом, добавление этого значения к 1.0 приведет к другому числу. Но добавление его к 2.5 приведет к , а не , в результате получится другое число, потому что разница между 2.5 и наименьшим числом с плавающей запятой, большим 2.5, будет на больше , чем у этого эпсилона. Таким образом, эпсилон усекается, как .00001 в приведенном выше примере.


Некоторые языки / библиотеки могут предлагать функцию «ulp», которая возвращает разницу между данным значением и следующим большим представимым значением. Например, в Java у вас есть

System.out.println(Math.ulp(1.0)); // Prints 2.220446049250313E-16
System.out.println(Math.ulp(2.5)); // Prints 4.440892098500626E-16

Первый, очевидно, хранится в Number.EPSILON. Второе значение - это значение, которое должно давать другое значение при добавлении к 2,5. Итак

  • 2.5 < 2.5 + 4.4408E-16 будет false и
  • 2.5 < 2.5 + 4.4409E-16 будет true
0 голосов
/ 04 июня 2019

Number.EPSILON:

разница между 1 и наименьшим числом с плавающей запятой, большим, чем 1

Допустим, это число равно 0,00000000000000000000000001 для аргумента аргумента.

Теперь,

1.5 < 1.5 + 0.00000000000000000000000001 === true

и, когда базовое число, которое вы добавляете в эту чрезвычайно малую дробь, увеличивается, точность вычисления математической оценки JS находит свою границу.

2 < 2 + 0.00000000000000000000000001 === false
...