Открыть сервис

Not a Number

Not a Number (NaN, с англ. — «не число») — это специальное значение в стандарте чисел с плавающей запятой IEEE 754, которое обозначает результат операции, не поддающийся математической интерпретации. NaN не является числом в обычном понимании, а представляет собой символьное обозначение ошибочного или неопределённого результата вычислений, такого как деление нуля на ноль, извлечение квадратного корня из отрицательного числа или логарифмирование отрицательного числа.

История

Понятие NaN было введено в стандарт IEEE 754 в 1985 году, который стал основой для представления чисел с плавающей запятой в большинстве современных компьютерных систем. До этого разные процессоры и языки программирования по-разному обрабатывали неопределённые результаты: одни генерировали исключения, другие возвращали нулевые значения, третьи — специальные коды ошибок. Введение NaN позволило унифицировать обработку таких ситуаций, сделав вычисления более предсказуемыми и переносимыми между разными архитектурами.

В стандарте IEEE 754-2008 и последующих версиях NaN был детализирован: определены два типа — тихий (quiet NaN, qNaN) и сигнальный (signaling NaN, sNaN), а также уточнено поведение при арифметических операциях и сравнениях.

Классификация

Тихий NaN (qNaN)

Тихий NaN — это значение, которое не вызывает исключения при использовании в арифметических операциях. Большинство операций с qNaN возвращают qNaN, что позволяет распространять ошибку по цепочке вычислений без немедленного прерывания программы. Тихий NaN используется для обозначения результата операций, которые не имеют математического смысла, например:

  • 0.0 / 0.0
  • sqrt(-1.0)
  • log(-1.0)

Сигнальный NaN (sNaN)

Сигнальный NaN — это значение, которое при попытке использования в арифметической операции или сравнении генерирует исключение (сигнал). sNaN предназначен для отладки и тестирования: его можно использовать для инициализации переменных, чтобы обнаружить их использование до присвоения корректного значения. В большинстве языков программирования sNaN не поддерживается напрямую, но может быть создан с помощью низкоуровневых операций.

Представление в памяти

В стандарте IEEE 754 числа с плавающей запятой представляются в виде трёх полей: знак (1 бит), порядок (экспонента, 8 бит для одинарной точности, 11 для двойной) и мантисса (23 или 52 бита соответственно). NaN кодируется как значение, в котором все биты порядка равны 1, а мантисса не равна нулю. Если старший бит мантиссы равен 1, то это тихий NaN, если 0 — сигнальный. Остальные биты мантиссы могут нести дополнительную информацию (payload), которая иногда используется для кодирования причины ошибки.

Пример для 32-битного числа:

  • Порядок: 11111111 (255)
  • Мантисса: 1xxx...x (не ноль) — тихий NaN
  • Мантисса: 0xxx...x (не ноль) — сигнальный NaN

Поведение в операциях

Арифметические операции

Любая арифметическая операция, в которой хотя бы один операнд является NaN, возвращает NaN. Исключение составляют операции, которые могут быть определены через NaN, например, max(NaN, x) и min(NaN, x) в некоторых реализациях возвращают x, а не NaN. Однако стандарт IEEE 754 рекомендует возвращать NaN для таких операций.

Сравнения

Сравнения с NaN имеют особенность: любое сравнение, включая NaN == NaN, возвращает false. Это означает, что NaN не равен самому себе. Для проверки, является ли значение NaN, используется функция isNaN() или проверка вида x != x (так как только NaN удовлетворяет этому условию). Сравнения с NaN с использованием операторов <, >, <=, >= также возвращают false.

Функции

Большинство математических функций, таких как sqrt, log, sin, cos, при передаче NaN возвращают NaN. Некоторые функции могут генерировать исключение при определённых условиях, но это зависит от реализации.

Применение

В языках программирования

NaN широко используется в языках программирования, поддерживающих числа с плавающей запятой:

  • JavaScript: глобальная переменная NaN и функция isNaN(). При арифметических операциях с нечисловыми значениями (например, "abc" * 2) возвращается NaN.
  • Python: float('nan') и math.nan. Проверка с помощью math.isnan().
  • C/C++: макрос NAN из <math.h> или <cmath>. Функция isnan().
  • Java: константа Double.NaN и Float.NaN. Проверка с помощью Double.isNaN().
  • Rust: f64::NAN и f32::NAN. Проверка с помощью is_nan().

В базах данных

В SQL стандарте NaN не определён, но некоторые СУБД, такие как PostgreSQL и MySQL, поддерживают его как специальное значение для чисел с плавающей запятой. В PostgreSQL NaN считается равным самому себе при сравнении, что отличается от стандарта IEEE 754.

В научных вычислениях

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

Критика и ограничения

Основная критика NaN связана с его поведением при сравнениях: NaN != NaN может приводить к неожиданным ошибкам в программах, особенно при сортировке или поиске. Например, в JavaScript [NaN].indexOf(NaN) возвращает -1, так как indexOf использует строгое сравнение. Для решения этой проблемы используются специальные функции, такие как Object.is() в JavaScript.

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

Интересные факты

  • В стандарте IEEE 754 существует около 2^53 различных значений NaN (для двойной точности), что позволяет использовать их для кодирования дополнительной информации, например, типа ошибки.
  • В некоторых языках, таких как Python, NaN может быть создан с помощью float('nan'), но при этом он не является константой и может иметь разные представления в памяти.
  • В JavaScript typeof NaN возвращает "number", что является исторической особенностью языка, связанной с тем, что NaN — это значение типа Number.
  • В некоторых реализациях, например, в MATLAB, NaN может быть использован для обозначения отсутствующих данных в матрицах, и функции, такие как sum() и mean(), игнорируют NaN по умолчанию.

Источники

  • IEEE Standard for Floating-Point Arithmetic (IEEE 754-2019)
  • «What Every Computer Scientist Should Know About Floating-Point Arithmetic» — David Goldberg, 1991
  • Документация языка JavaScript (MDN Web Docs) — раздел «NaN»
  • Документация языка Python (Python Software Foundation) — раздел «math.nan»
  • Стандарт SQL:2016, часть 2: Foundation (SQL/Foundation)

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →