Почему typeof NaN возвращает «число»?


166

Просто из любопытства.

Это не кажется очень логичным, что typeof NaNчисло. Прямо как NaN === NaNили NaN == NaNвозвращение ложного, кстати. Это одна из особенностей javascript, или была бы причина для этого?

Изменить: спасибо за ваши ответы. Это не легкая вещь, чтобы заставить их голову вокруг все же. Читая ответы и вики я понял больше, но все же предложение вроде

Сравнение с NaN всегда возвращает неупорядоченный результат даже при сравнении с самим собой. Предикаты сравнения являются либо сигнальными, либо не сигнальными, версии сигнализации сигнализируют недопустимое исключение для таких сравнений. Предикаты равенства и неравенства не сигнализируют, поэтому x = x, возвращающий false, можно использовать для проверки, является ли x спокойным NaN.

Просто у меня голова кружится. Если бы кто-то смог перевести это на понятный человеку (в отличие от, скажем, математика) язык, я был бы благодарен.


17
+1: "NaN - это число, но не число. Хм ... что ?!"
2010 года

11
Для дополнительного удовольствия; (NaN! == NaN) == верно
Алекс К.

1
Еще более увлекательным (но понятно, если подумать, isNaN возвращает логическое значение): isNaN (parseInt ('nodice')) === isNaN (parseInt ('someOtherNaN')) === true;
KooiInc

1
Если вы используете jQuery, я предпочитаю isNumericпроверять тип: $.isNumeric(NaN); возвращает false, где as $.type(NaN);возвращает номер. api.jquery.com/jQuery.isNumeric
Джастин

3
Как профессиональный математик, я должен сказать, что предложение имеет мало общего с точным языком математики.
Дмитрий Зайцев

Ответы:


53

Это не число. Это не особенность javascript, а общий принцип информатики.

С http://en.wikipedia.org/wiki/NaN :

Существует три вида операций, которые возвращают NaN:

Операции с NaN в качестве хотя бы одного операнда

Неопределенные формы

  • Деления 0/0, ∞ / ∞, ∞ / −∞, −∞ / ∞ и −∞ / −∞
  • Умножения 0 × ∞ и 0 × −∞
  • Степень 1 ^ ∞
  • Сложения ∞ + (−∞), (−∞) + ∞ и эквивалентные вычитания.

Реальные операции со сложными результатами:

  • Квадратный корень из отрицательного числа
  • Логарифм отрицательного числа
  • Тангенс нечетного кратного 90 градусов (или π / 2 радиан)
  • Обратный синус или косинус числа, которое меньше -1 или больше +1.

Все эти значения могут не совпадать. Простой тест для NaN - это проверка value == valueна ложность.


46
Еще более простой тестisNaN(value)
Альсьенде

4
@Alsciende это не эквивалентно, хотя. isNaN(undefined)возвращается true, но undefined == undefinedтоже верно. То же самое касается всех других нечисловых типов, кроме null.
Энди

7
Другими словами, value !== valueэто, вероятно, самый короткий способ проверить, valueдействительно ли это так NaN.
Энди

1
Похоже, ты прав, @Анди. Теперь это особенность.
Альсиенде

2
Фраза «эти значения не могут быть одинаковыми» не имеет значения, потому что эти значения не существуют.
Дмитрий Зайцев

103

Ну, NaNэто все еще числовой тип , несмотря на то, что на самом деле он обозначает Not-A-Number :-)

NaNпросто означает, что конкретное значение не может быть представлено в пределах ограничений числового типа (хотя это можно сказать для всех чисел, которые должны быть округлены, чтобы соответствовать, но NaNэто особый случай).

Определенное NaNне считается равным другому, NaNпоскольку они могут иметь разные значения. Тем не менее, NaNэто все еще числовой тип, как 2718 или 31415.


Что касается вашего обновленного вопроса, чтобы объяснить в терминах непрофессионала:

Сравнение с NaN всегда возвращает неупорядоченный результат даже при сравнении с самим собой. Предикаты сравнения являются либо сигнальными, либо не сигнальными, версии сигнализации сигнализируют недопустимое исключение для таких сравнений. Предикаты равенства и неравенства не сигнализируют, поэтому x = x, возвращающий false, можно использовать для проверки, является ли x спокойным NaN.

Все это означает (разбито на части):

Сравнение с NaN всегда возвращает неупорядоченный результат даже при сравнении с самим собой.

По сути, a NaNне равно любому другому числу, включая другое NaN, и даже включая себя .

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

Попытка выполнить операции сравнения (меньше, больше, и т. Д.) Между NaNчислом a и другим может привести к тому, что будет сгенерировано исключение (сигнализация) или просто получится ложное в результате (не сигнализация или тишина).

Предикаты равенства и неравенства не сигнализируют, поэтому x = x, возвращающий false, можно использовать для проверки, является ли x спокойным NaN.

Тесты на равенство (равно, не равно) никогда не сигнализируют, поэтому их использование не вызовет исключения. Если у вас есть обычный номер x, то x == xвсегда будет верно. Если xесть NaN, то x == xвсегда будет ложным. Это дает вам возможность NaNлегко обнаружить (тихо).


1
хорошее объяснение, хотя я не согласен в последних двух предложениях: лучший способ проверить, является ли x NaN, использует функцию isNaN ()
Карлос Барселона,

@DominicRodger вот как я об этом думаю: typeof a === 'number'означает, что «хранится внутри как IEEE 754 float»
Энди

Почему Infinity === Infinityвозвращается, trueесли an Infinityможет быть получено с разными значениями: 1.0 / 0.0 или 2.0 / 0.0?
Хашем Колами,

1
@ Хашем, вполне вероятно, потому что они считаются одной и той же бесконечностью. Рассматривая деление как повторное вычитание, не имеет значения, начинаете ли вы с двух или одного, это то же самое количество шагов, которое необходимо для достижения (или, точнее, не достижения) нуля. Я понимаю, что математические гуру имеют разные классы бесконечности, но (1) я подозреваю 1/0и 2/0лежу в одном классе и (2) в IEEE754 есть только один класс бесконечности (кроме, +/-конечно,).
paxdiablo

1
Я не знаю ни одного способа определить эти исключительные «фактические числа» каким-либо значимым образом. В математике журнал и корень отрицательных значений можно получить только путем расширения действительных чисел до комплексных чисел, где они оцениваются по нескольким значениям, и 0/0не определены каким-либо осмысленным образом, за исключением того, что его «значением» является весь набор чисел. И даже если они были определены, Math.log(-1) == Math.log(-1)все равно оценивается false. Таким образом, не только не существует «фактических чисел», NaNно даже если они были, они не использовались для сравнения.
Дмитрий Зайцев

20

Стандарт ECMAScript (JavaScript) указывает, что Numbersэто IEEE 754 с плавающей точкой, которые включают NaNв качестве возможного значения.

ECMA 262 5e Раздел 4.3.19 : Числовое значение

значение примитива, соответствующее 64-битному двоичному формату IEEE 754 с двойной точностью.

ECMA 262 5e Раздел 4.3.23 : NaN

Числовое значение, которое является значением IEEE 754 "Not-a-Number".

IEEE 754 в Википедии

Стандарт IEEE для арифметики с плавающей точкой является техническим стандартом, установленным Институтом инженеров по электротехнике и электронике, и наиболее широко используемым стандартом для вычислений с плавающей точкой [...]

Стандарт определяет

  • арифметические форматы : наборы двоичных и десятичных данных с плавающей запятой, которые состоят из конечных чисел (в том числе нулей со знаком и субнормальных чисел), бесконечностей и специальных значений «не число» (NaN)

[...]


8

typeof NaNвозвращается, 'number'потому что:

  • Спецификация ECMAScript говорит, что тип Number включает NaN:

    4.3.20 Тип номера

    набор всех возможных числовых значений, включая специальные значения «Not-a-Number» (NaN), положительную бесконечность и отрицательную бесконечность

  • Так что typeofвозвращается соответственно:

    11.4.3 Тип оператора

    Производство UnaryExpression : typeof UnaryExpression оценивается следующим образом:

    1. Пусть val будет результатом вычисления UnaryExpression .
    2. Если Тип ( val ) является Ссылкой , тогда
      1. Если IsUnresolvableReference ( val ) имеет значение true , вернуть "undefined".
      2. Пусть val будет GetValue ( val ).
    3. Возвращает строка определяется типом ( вали ) в соответствии с таблицей 20.

                    Table 20 — typeof Operator Results
    ==================================================================
    |        Type of val         |              Result               |
    ==================================================================
    | Undefined                  | "undefined"                       |
    |----------------------------------------------------------------|
    | Null                       | "object"                          |
    |----------------------------------------------------------------|
    | Boolean                    | "boolean"                         |
    |----------------------------------------------------------------|
    | Number                     | "number"                          |
    |----------------------------------------------------------------|
    | String                     | "string"                          |
    |----------------------------------------------------------------|
    | Object (native and does    | "object"                          |
    | not implement [[Call]])    |                                   |
    |----------------------------------------------------------------|
    | Object (native or host and | "function"                        |
    | does implement [[Call]])   |                                   |
    |----------------------------------------------------------------|
    | Object (host and does not  | Implementation-defined except may |
    | implement [[Call]])        | not be "undefined", "boolean",    |
    |                            | "number", or "string".            |
    ------------------------------------------------------------------

Это поведение соответствует стандарту IEEE для арифметики с плавающей точкой (IEEE 754) :

4.3.19 Числовое значение

значение примитива, соответствующее 64-битному двоичному формату IEEE 754 с двойной точностью

4.3.23 NaN

числовое значение, которое является значением IEEE 754 «Not-a-Number»

8.5 Тип номера

Тип Number имеет ровно 18437736874454810627 (то есть 2 53 -2 64 +3) значения, представляющие значения IEEE 754 с 64-разрядным форматом двойной точности, как указано в стандарте IEEE для двоичной арифметики с плавающей запятой, за исключением того, что 9007199254740990 ( то есть, 2 53 -2) различные значения «Not-a-Number» стандарта IEEE представлены в ECMAScript как одно специальное значение NaN . (Обратите внимание, что значение NaN создается выражением программы NaN.)


5

NaN является допустимым значением с плавающей запятой ( http://en.wikipedia.org/wiki/NaN )

и NaN === NaN ложно, потому что они не обязательно одинаковы


1
Извините, но я должен сказать, что это не очень хороший способ думать об этом. «не обязательно один и тот же номер» не означает, что они всегда разные, и сравнение их должно давать ложь. Лучше не давать количественную оценку NaN, а просто думать об этом как о странности в нашей базе знаний.
Дейв

1
Так почему же все они Infinityкак-то идентичны? Есть предположения?
Хашем Колами,

5

NaN != NaNпотому что они не нужны ЖЕ не номер. Таким образом, это имеет большой смысл ... Кроме того, почему числа с плавающей точкой имеют +0,00 и -0,00, которые не совпадают. Округление может сделать то, что они на самом деле не равны нулю.

Что касается typeof, это зависит от языка. И большинство языков скажут, что NaN - это число с плавающей запятой, двойное число или число, в зависимости от того, как они его классифицируют ... Я не знаю языков, которые бы говорили, что это неизвестный тип или нуль.


1
эх, рассмотрим: var x = parseInt ('no dice'), y = x; Теперь я бы сказал, что оба NaN одинаковы? Но нет, x === y тоже возвращает false.
KooiInc

да, но вы не можете быть уверены, и, следовательно, они не одинаковы. Это та же логика, что и логики NULLable в базе данных. Хотя многие люди считают их такими же, как нулевые указатели из других языков программирования, в действительности они имеют совершенно другую семантику. Они являются «НЕИЗВЕСТНЫМИ», и поэтому одно значение NULL по сравнению с другим всегда ложно. Выполнение вычислений для значения NULL заканчивается результатом NULL. Попробуйте взглянуть на это с точки зрения того, что значение называется НЕИЗВЕСТНЫМ
Cine

Как тип number, NaNявляется примитивным, и, следовательно, однозначно определяется его значением.
Дмитрий Зайцев

4

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

Хотя его имена говорят, что это не число, тип данных, используемый для его хранения, является числовым типом. Таким образом, в JavaScript запрос типа данных NaNвернется number(как alert(typeof(NaN))наглядно демонстрирует).


На самом деле деление на ноль оценивается как InfinityнетNaN
Дмитрий Зайцев

2

Javascript использует NaN для представления всего, с чем он сталкивается, что не может быть представлено другими способами в его спецификациях. Это не значит, что это не число. Это просто самый простой способ описать встречу. NaN означает, что он или объект, который ссылается на него, не может быть представлен каким-либо другим способом с помощью javascript. Для всех практических целей, это «неизвестно». Будучи «неизвестным», он не может сказать вам, что это такое, даже если оно само по себе. Это даже не объект, которому он назначен. Он может только сказать вам, что это не так, а не-ничто или ничто можно описать математически только на языке программирования. Поскольку математика о числах, javascript представляет ничто как NaN. Это не значит, что это не число. Это означает, что мы не можем прочитать это любым другим способом, который имеет смысл. Вот почему это может т даже равен себе. Потому что это не так.


2

Лучшее название NaN, описывающее его значение более точно и менее запутанно, будет числовым исключением . Это действительно другой тип объекта исключения, замаскированный как имеющий примитивный тип (в соответствии с языковым дизайном), где в то же время он не рассматривается как примитивный в своем ложном самосравнении. Откуда путаница. И до тех пор, пока язык «не решится» выбирать между правильным объектом исключения и примитивной цифрой , путаница останется.

Печально известное неравенство между NaNсобой и тем ==и другим ===является проявлением запутанного замысла, заставляющего этот объект исключения становиться примитивным типом. Это нарушает фундаментальный принцип, согласно которому примитив однозначно определяется его ценностью . Если NaNпредпочтительнее рассматривать его как исключение (из которых могут быть разные виды), то его не следует «продавать» как примитив. И если он хочет быть примитивным, этот принцип должен соблюдаться. Пока он сломан, как у нас в JavaScript, и мы не можем на самом деле выбрать между ними, путаница, приводящая к ненужной когнитивной нагрузке для всех участников, останется. Что, однако, действительно легко исправить, просто сделав выбор между двумя:

  • либо создать NaNспециальный объект исключения, содержащий полезную информацию о том, как возникло исключение, а не выбрасывать эту информацию как то, что в настоящее время реализовано, что приводит к сложному для отладки коду;
  • или сделать NaNобъект примитивного типа number(который можно было бы назвать менее численным) «в числовом виде», в этом случае он должен быть равен самому себе и не может содержать никакой другой информации; последнее явно является худшим выбором.

Единственное мыслимое преимущество принудительного ввода NaNв numberтип - это возможность вернуть его в любое числовое выражение. Что, однако, делает его хрупким выбором, потому что результат любого содержащего числовое выражение NaNбудет либо иметь NaN, либо приводить к непредсказуемым результатам, таким как NaN < 0оценка false, т.е. возврат booleanвместо сохранения исключения.

И даже если «все так, как есть», ничто не мешает нам сделать это четкое различие для себя, чтобы сделать наш код более предсказуемым и легче отлаживаемым. На практике это означает выявление этих исключений и рассмотрение их как исключений. Что, к сожалению, означает больше кода, но, надеюсь, будет смягчено такими инструментами, как TypeScript of Flowtype.

И затем у нас есть грязное тихое против шумного ака сигнального NaNразличия . Что на самом деле касается того, как обрабатываются исключения, а не самих исключений, и ничем не отличается от других исключений.

Аналогично, Infinityи +Infinityявляются элементами числового типа, возникающими в расширении реальной строки, но они не являются действительными числами. Математически они могут быть представлены последовательностями действительных чисел, сходящихся к одному +или -Infinity.


1

Это просто потому, что NaNэто свойство объекта Number в JS. Оно не имеет ничего общего с тем, что оно является числом.


Как и любой другой объект, Number может иметь любой тип свойства. Number.fu = "bar"; alert(typeof Number.fu);
Alsciende

NaNне является значением, хранящимся в нем Number.NaN, каким бы оно ни было. NaNявляется примитивным значением типа Number. И, кроме того, значение Number.NaNесть NaN, но это не связано.
Oriol

1

Лучший способ думать о NAN - то, что это не известное число. Вот почему NAN! = NAN, потому что каждое значение NAN представляет какое-то уникальное неизвестное число. NAN необходимы, потому что числа с плавающей запятой имеют ограниченный диапазон значений. В некоторых случаях происходит округление, когда младшие биты теряются, что приводит к тому, что кажется бессмысленным, например, 1,0 / 11 * 11! = 1,0. Действительно большие значения, которые больше, являются NAN с бесконечностью, являющейся прекрасным примером.

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


Бесконечность не представлена ​​NaN. Попытка представить число вне диапазона будет округляться в меньшую сторону (до max / -inf) или вверх (до min / + inf).
OrangeDog


1

NaNявляется числом с точки зрения типа, но не является нормальным числом, таким как 1, 2 или 329131. Имя «Не число» относится к тому факту, что представленное значение является специальным и относится к области спецификации формата IEEE, а не к домен языка javascript.


1

Если вы используете jQuery, я предпочитаю isNumericпроверять тип:

console.log($.isNumeric(NaN));  // returns false
console.log($.type(NaN));       // returns number

http://api.jquery.com/jQuery.isNumeric/


Спасибо чувак. У меня были проблемы с isNumberиз utilпакета машинописи. Хорошо, что мы все еще используем jQueryв нашем проекте, поэтому использовал ваше предложение вместо этого.
Ашок М.А.

Для записи, isNumberиз utilмашинописи также возвращает trueдля NaN.
Ашок М.А.

0

Javascript имеет только один числовой тип данных, который является стандартным 64-битным плавающим с двойной точностью. Все двойное. NaN - это особое значение double, но, тем не менее, это double.

Все, что parseIntнужно, это «привести» вашу строку к числовому типу данных, так что результатом всегда будет «число»; только если исходная строка не может быть проанализирована, ее значение будет NaN.


0

NaN по-прежнему числовой тип, но он представляет значение, которое не может представлять допустимое число.


0

Мы можем утверждать, что NaN является объектом особого случая. В этом случае объект NaN представляет число, которое не имеет математического смысла. В математике есть и другие особые объекты, такие как INFINITE и так далее.

Вы все еще можете сделать некоторые вычисления с ним, но это приведет к странному поведению.

Более подробная информация здесь: http://www.concentric.net/~ttwang/tech/javafloat.htm (на основе Java, а не на JavaScript)


0

Вы должны любить Javascript. У этого есть некоторые интересные маленькие причуды.

http://wtfjs.com/page/13

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

Между прочим, я рекомендую прочитать остальную часть http://wtfjs.com/ - есть намного более интересные причуды, чем этот, чтобы быть найденным!


0

Значение NaN - это действительно Number.NaN, поэтому, когда вы спросите, является ли это число, он скажет «да». Вы сделали правильную вещь, используя вызов isNaN ().

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


В каком смысле это «ценность»? NaN == Number.NaNоценивает до false!
Дмитрий Зайцев

@DmitriZaitsev Вы читали ветку? А вы пробовали если (parseInt ("nan") == Number.NaN)? Также попробуйте! = И посмотрите, что он говорит вам.
Роб

Извините, забыл глупое NaN==NaNсущество false, должно быть , это был садист, который изобрел это, чтобы заставить всех страдать.
Дмитрий Зайцев

0

Пример

Представьте, что мы конвертируем строку в число:

Number("string"); // returns NaN

Мы изменили тип данных на число, но его значение не является числом!


Вы, кажется, упустили суть вопроса. NaNимеет тип номера . Вопрос в том, почему.
Квентин,

@Quentin Я объяснил в последней строке.
Амир Фо

-1

Это специальное значение типа Number как POSITIVE_INFINITY

Зачем? По дизайну

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.