Комментарии 26
Насколько я понимаю, то что Nan!=Nan - требование IEEE 754. Но почему так сделали? ИМХО NaN это обычный none в опциональном типе, а создатели стандарта чисел с плавающей точкой все зачем-то усложнили.
NaN и отсутствие числа - разные вещи
Maybe Float
всем лучше! Обнаружил тут крейт Noisy Float, не такой уж непопулярный, который как раз-таки с безопасным float
UPD: безопасный он довольно условно: просто паникует при любом ненормальном числе. Видимо, это поведение более востребовано, чем аккуратный Option
вокруг почти всего
Возможно, потому же, что в SQL NULL <> NULL
NaN получается в результате разных операций. Например вы что-то поделили на 0 и получили NaN и вы взяли квадратный корень из -1 и получили NaN. Язык не может гарантировать равенство результатов, которые он не может выразить числом.
Я думаю имелось ввиду, что /0=∞, а -1^1/2 — мнимое число. Т.е результаты разные, но представлены одним и тем же NaN, который эдакий "всё плохо". И чтобы не сравнивали разные виды "всё плохо" с друг другом...
Выражусь более понятно. Вы же согласны, что следующее сравнение не является истинным с точки зрения математики:
if (1/0 == Math.sqrt(-1)) {
console.log('1/0 equal to sqrt(-1)');
}
А теперь замените левую и правую часть сравнения на NaN. Почему в этом результат должен отличаться.
Потому что JS не умеет комплексные числа, и так как они не могут быть выражены через тип number, то мы имеем NaN. И поскольку JS не может корректно представить левую и правую часть типом number он заменяет их на NaN.
Если в перспективе number расширят на комплексные числа или введут новый тип, то и sqrt(-1) перестанет быть NaN, но пока так.
Перечитайте limits/пределы из математики (стремящиеся к бесконечности, например) и тогда поймёте почему разные Nan разные (в общем случае).
>> не являются числами
Совершенно верно! Nan - Not a number ))
https://uk.wikipedia.org/wiki/NaN - там все написано, если не верите мне
Был бы явный Option, было бы логично: (Some(5) == Some(5)) == Some(true)
, при этом (Some(5) == None) == None)
и (None == None) = None)
.
Но Option
тут половинчатый, и такое решение выглядит довольно логично. В конце концов, NaN
представляет разные ситуации не-чисел, например, parseInt("blah")
и "".charCodeAt(1)
. Было бы странно говорить, что они равны в контексте сравнения чисел.
Хороший вопрос! Для меня сравнение тут возможно трёх видов (Number
во всех трёх случаях включает не NaN
, Option<Number>
— включает):
Option<Number> -> Option<Number> -> Option<Bool>
, когда сравниваются только числа, а не-числа приводят к принципиально другому результату. Это как в SQL с nullable-типами:NULL = NULL
даётNULL
Option<Number> -> Option<Number> -> Bool
по типу SQL-ногоIS NOT DISTINCT FROM
(оно же<=>
в MySQL). Это Ваш вариантOption<Number> -> Option<Number> -> Bool
, в виде модификации первого варианта, когдаSome(true)
приравнивается кtrue
, а всё остальное — кfalse
. Это вариант из IEEE 754. Кстати, преобразованиеSome<Bool>
вBool
здесь по аналогии с работойWHERE
в SQL: отбираются только строки со значением условияTRUE
, аFALSE
иNULL
— нет, происходит эдакое впихивание трёх значенийOption<Bool>
в два значенияBool
.
Я думаю о том, что блок с условием NaN == NaN
не выполнится, по аналогии с тем, как в SQL
не отберутся строки с NULL = NULL
.
Кстати, читал тут документацию, и почему-то NaN ** 0
в Javascript даёт единицу. Вероятно, потому что нулевая степень чего угодно, даже жаренной с грибами картошки, даёт всё равно единицу.
~==~
выглядит ровно как сравнение в SQL с учётом nullable, на этот счёт недавно была полемическая статья.
Я совершенно согласен, что в целом для Maybe
обыкновенный ==
выглядит удобнее почти во всех случаях.
Кстати, в IEEE 754 прочитал (раздел 5.11):
Four mutually exclusive relations are possible: less than, equal, greater than, and unordered. The last case arises when at least one operand is NaN. Every NaN shall compare unordered with everything, including itself.
Соответственно, для каждой операции сравнения разный набор этих вариантов превращается в true
или false
, там ниже таблицы истинности.
Как это работает — понятно, а что это с точки зрения типов и как это думать — перестал понимать совсем.
Решение проблемы — запрет на прямое сравнение с NaN
Теперь разработчики видят ошибку
Но ведь к NaN, как было написано, может приводить любая операция и процедура. Каким образом статический анализатор это поймёт? (прошу прощения если вопрос глупый, не спец по JS)
речь идёт про TypeScript, тоесть отлавливаться данная проверка будет на этапе транспиляции
я думаю, он не все словит, скорей всего даже if (1/0 === 2/0)
пропустит
Это, во многом, нацелено именно на сравнение с константой NaN
. Потому что на автомате часто пишут x != NaN
для проверки на NaN
, но эта проверка по правилам IEEE754 всегда истинна. Теперь случаи такой проверки будут подсвечены как ошибки.
TypeScript 4.9: что нас ожидает