Странное поведение nan в python [duplicate]

Вы можете использовать TestSuite и пользовательские классы TestCase.

import unittest

class CustomTest(unittest.TestCase):
    def __init__(self, name, a, b):
        super().__init__()
        self.name = name
        self.a = a
        self.b = b

    def runTest(self):
        print("test", self.name)
        self.assertEqual(self.a, self.b)

if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTest(CustomTest("Foo", 1337, 1337))
    suite.addTest(CustomTest("Bar", 0xDEAD, 0xC0DE))
    unittest.TextTestRunner().run(suite)
210
задан CSᵠ 19 September 2014 в 17:13
поделиться

13 ответов

Я был членом комитета IEEE-754, я попытаюсь немного разъяснить некоторые вещи.

Во-первых, числа с плавающей запятой не являются действительными числами, а арифметика с плавающей запятой не удовлетворяют аксиомам реальной арифметики. Трихотомия - это не единственное свойство реальной арифметики, которое не выполняется для поплавков и даже самое важное. Например:

  • Дополнение не является ассоциативным.
  • Закон распределения не выполняется.
  • Имеются числа с плавающей запятой без инверсий.

Я мог бы продолжить. Невозможно указать арифметический тип фиксированного размера, который удовлетворяет всем свойствам реальной арифметики, которые мы знаем и любим. Комитет 754 должен решить склонить или сломать некоторые из них. Это руководство основано на некоторых довольно простых принципах:

  1. Когда мы можем, мы сопоставляем поведение реальной арифметики.
  2. Когда мы не можем, мы пытаемся сделать нарушения как можно предсказуемо и легко диагностировать.

Что касается вашего комментария, «это не означает, что правильный ответ ложный», это неправильно. Предикат (y < x) спрашивает, меньше ли y, чем x. Если y является NaN, то это не меньше любого значения с плавающей запятой x, поэтому ответ обязательно является ложным.

Я упомянул, что трихотомия не выполняется для значений с плавающей запятой. Однако есть аналогичное свойство, которое выполняется. Пункт 5.11, параграф 2 стандарта 754-2008:

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

Что касается написания дополнительного кода для обработки NaNs, обычно возможно (хотя и не всегда легко) структурировать ваш код в таком формате, что NaNs проваливаются должным образом, но это не всегда так. Когда это не так, может потребоваться некоторый дополнительный код, но это небольшая цена, чтобы заплатить за удобство, которое привело к алгебраическому закрытию для арифметики с плавающей запятой.


Приложение: Многие комментаторы утверждали, что было бы более полезно сохранить рефлексивность равенства и трихотомии на том основании, что принятие NaN! = NaN, похоже, не сохраняет какой-либо знакомой аксиомы. Я признаюсь, что имею некоторое сочувствие к этой точке зрения, поэтому я решил вернуться к этому ответу и предоставить немного больше контекста.

Мое понимание от разговоров с Каханом заключается в том, что NaN! = NaN возникла из двух прагматических соображений :

  • То, что x == y должно быть эквивалентно x - y == 0, когда это возможно (не являясь теоремой реальной арифметики, это делает аппаратную реализацию сравнения более эффективной с точки зрения пространства, что имеет первостепенное значение при время, когда стандарт был разработан - обратите внимание, однако, что это нарушается при x = y = бесконечность, поэтому это не является большой причиной для себя, оно может быть разумно согнуто до (x - y == 0) or (x and y are both NaN)).
  • Что еще более важно, предикат isnan( ) не существовал в то время, когда NaN был формализован в арифметике 8087; необходимо было предоставить программистам удобное и эффективное средство обнаружения значений NaN, которые не зависят от языков программирования, предоставляющих нечто вроде isnan( ), которое может занять много лет. Я процитирую собственное письмо Кахана по этому вопросу:

Если бы не было способа избавиться от NaN, они были бы столь же бесполезны, как и Indefinites на CRAY; как только кто-то столкнулся, расчет лучше всего прекратить, а не продолжиться на неопределенное время до окончательного вывода. Вот почему некоторые операции над NaN должны доставлять результаты, отличные от NaN. Какие операции? ... Исключениями являются предикаты C «x == x» и «x! = X», которые соответственно равны 1 и 0 для каждого бесконечного или конечного числа x, но наоборот, если x не является числом (NaN); они обеспечивают единственное простое исключающее различие между NaN и числами на языках, которым не хватает слова для NaN и предиката IsNaN (x).

Обратите внимание, что это также логика, которая исключает возвращение чего-либо как «Not-A-Boolean». Возможно, этот прагматизм был неуместным, и стандарт должен был потребовать isnan( ), но это сделало бы NaN почти невозможным использовать эффективно и удобно в течение нескольких лет, пока мир ждал принятия языка программирования. Я не уверен, что это был бы разумный компромисс.

Чтобы быть тупым: результат NaN == NaN теперь не изменится. Лучше научиться жить с ним, чем жаловаться в Интернете. Если вы хотите утверждать, что отношение порядка, подходящее для контейнеров, должно существовать также , я бы рекомендовал, чтобы ваш любимый язык программирования реализовал предикат totalOrder, стандартизованный в IEEE-754 (2008). Тот факт, что он еще не говорит о справедливости озабоченности Кахана, которая мотивировала нынешнее положение дел.

429
ответ дан Peter Cordes 27 August 2018 в 03:03
поделиться

NaN можно рассматривать как неопределенное состояние / число. аналогично понятию 0/0, которое не определено или sqrt (-3) (в системе реальных чисел, где живет плавающая точка).

NaN используется как своего рода заполнитель для этого неопределенного состояния. Математически говоря, undefined не равен undefined. Вы также не можете сказать, что неопределенное значение больше или меньше другого неопределенного значения. Поэтому все сравнения возвращают false.

Это поведение также выгодно в случаях, когда вы сравниваете sqrt (-3) с sqrt (-2). Они оба вернут NaN, но они не эквивалентны, даже если они возвращают одинаковое значение. Поэтому, когда равенство всегда возвращает false при работе с NaN, это желаемое поведение.

42
ответ дан brainjam 27 August 2018 в 03:03
поделиться

Я предполагаю, что NaN (Not A Number) означает именно это: это не число, и поэтому сравнение его действительно не имеет смысла.

Это немного похоже на арифметику в SQL с null: все они приводят к null.

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

0
ответ дан Daren Thomas 27 August 2018 в 03:03
поделиться

Более упрощенный ответ заключается в том, что NaN не имеет числового значения, поэтому нет ничего в этом, чтобы сравнивать с чем-либо еще.

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

0
ответ дан David R Tribble 27 August 2018 в 03:03
поделиться

NaN - неявный новый экземпляр (особый вид ошибки времени выполнения). Это означает NaN !== NaN по той же причине, что new Error !== new Error;

И помните, что такой имплициент также встречается вне ошибок, например, в контексте регулярных выражений это означает /a/ !== /a/, который является просто синтаксическим сахаром для new RegExp('a') !== new RegExp('a')

0
ответ дан Ivan Castellanos 27 August 2018 в 03:03
поделиться

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

NaN не содержит информации о том, что что-то есть, что это такое «т. Поэтому эти элементы нельзя однозначно утверждать равными.

29
ответ дан Jack Ryan 27 August 2018 в 03:03
поделиться

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

  • (2.7 == 2.7) = true
  • (2.7 == 2.6) = false
  • (2.7 == NaN) = неизвестно
  • (NaN == NaN) = неизвестно

Даже .NET не предоставляет оператора bool? operator==(double v1, double v2) , так что вы все еще придерживаетесь глупых результатов (NaN == NaN) = false.

2
ответ дан Mark Dickinson 27 August 2018 в 03:03
поделиться

Хотя я согласен, что сравнение NaN с любым реальным числом должно быть неупорядоченным, я думаю, что есть просто причина для сравнения NaN с самим собой. Как, например, можно обнаружить разницу между сигнальными NaN и тихими NaN? Если мы думаем о сигналах как о наборе булевых значений (т. Е. Битовом векторе), можно спросить, являются ли битовые векторы одинаковыми или разными и упорядочивают их соответственно. Например, при декодировании максимального смещенного экспоненты, если значение было сдвинуто влево, чтобы выровнять самый старший бит значащего в наиболее значимом бите двоичного формата, отрицательным значением будет тихое NaN, и любое положительное значение будет быть сигнальным NaN. Конечно, нуль зарезервирован для бесконечности, и сравнение будет неупорядоченным. Выравнивание MSB позволило бы прямое сравнение сигналов даже из разных двоичных форматов. Таким образом, два NaN с одинаковым набором сигналов будут эквивалентны и придают смысл равенству.

0
ответ дан Patrick Campbell 27 August 2018 в 03:03
поделиться

Для меня самый простой способ объяснить это:

У меня что-то есть, и если это не яблоко, то это оранжевый?

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

У меня что-то есть, и если оно не равно числу, это строка?

-2
ответ дан pushkin 27 August 2018 в 03:03
поделиться

Я не знаю конструктивного обоснования, но вот выдержка из стандарта IEEE 754-1985:

«Должно быть возможно сравнить числа с плавающей запятой во всех поддерживаемых форматах, даже если форматы операндов различаются. Сравнение точны и никогда не переполняются и не переполняются. Возможны четыре взаимоисключающих отношения: меньше, равно, больше и неупорядочено. Последний случай возникает, когда хотя бы один операнд - NaN. Каждый NaN будет сравнивать неупорядоченные с все, включая себя ».

4
ответ дан Rick Regan 27 August 2018 в 03:03
поделиться

Потому что математика - это поле, где числа «просто существуют». При вычислении вы должны инициализировать эти числа и сохранить свое состояние в соответствии с вашими потребностями. В те старые времена инициализация памяти работала так, как вы никогда не могли положиться. Вы никогда не могли позволить себе подумать об этом «о, это будет инициализировано с помощью 0xCD все время, мой алгоритм не сломается» .

Итак, вам нужно правильно не смешивающий растворитель, который достаточно липкий , чтобы не позволить вашему алгоритму всасываться и разрушаться. Хорошие алгоритмы с участием чисел в основном будут работать с отношениями, а отношения if () будут опущены.

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

Затем, когда вы все еще внезапно обнаруживаете, что ваш алгоритм производит NaNs, его можно очистить, глядя в каждую ветвь по одному. Опять же, правило «всегда ложное» очень помогает в этом.

-1
ответ дан sanaris 27 August 2018 в 03:03
поделиться
-2
ответ дан SeF 27 August 2018 в 03:03
поделиться

Из статьи wikipedia в NaN следующие методы могут приводить к NaNs:

  • Все математические операции> с NaN как по крайней мере одним операндом
  • Разделы 0/0, ∞ / ∞, ∞ / -∞, -∞ / ∞ и -∞ / -∞
  • Умножения 0 × ∞ и 0 × -∞
  • Добавления ∞ + (-∞), (-∞) + ∞ и эквивалентные вычитания.
  • Применение функции к аргументам вне ее домена, включая взятие квадратного корня из отрицательного числа, взятие логарифм отрицательного числа, взяв касательную нечетного кратного 90 градусов (или π / 2 радиан) или взяв обратный синус или косинус числа, которое меньше -1 или больше +1.

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

12
ответ дан Stefan Rusek 27 August 2018 в 03:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: