Почему float ('nan') == float ('nan') возвращает False? [Дубликат]

С new_list = my_list у вас фактически нет двух списков. Назначение просто копирует ссылку на список, а не фактический список, поэтому оба new_list и my_list относятся к тому же списку после назначения.

Чтобы на самом деле скопировать список, у вас есть различные возможности :

  • Вы можете использовать встроенный метод list.copy() (доступный с python 3.3):
    new_list = old_list.copy()
    
  • Вы можете отрезать его:
    new_list = old_list[:]
    
    мнение Алексея Мартелли (по крайней мере, в 2007 году ) об этом означает, что это странный синтаксис, и нет смысла использовать его когда-либо . ;) (По его мнению, следующий более читабель).
  • Вы можете использовать встроенную функцию list() :
    new_list = list(old_list)
    
  • Вы можете использовать общий copy.copy() :
    import copy
    new_list = copy.copy(old_list)
    
    Это немного медленнее, чем list(), потому что сначала он должен узнать тип данных old_list.
  • Если список содержит объекты, и вы также хотите их скопировать, используйте generic copy.deepcopy() :
    import copy
    new_list = copy.deepcopy(old_list)
    
    Очевидно, самый медленный и самый необходимый для памяти способ, но иногда неизбежный.

Пример:

import copy

class Foo(object):
    def __init__(self, val):
         self.val = val

    def __repr__(self):
        return str(self.val)

foo = Foo(1)

a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)

# edit orignal list and instance 
a.append('baz')
foo.val = 5

print('original: %r\n list.copy(): %r\n slice: %r\n list(): %r\n copy: %r\n deepcopy: %r'
      % (a, b, c, d, e, f))

Результат:

original: ['foo', 5, 'baz']
list.copy(): ['foo', 5]
slice: ['foo', 5]
list(): ['foo', 5]
copy: ['foo', 5]
deepcopy: ['foo', 1]

209
задан 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 21 August 2018 в 06:58
поделиться
  • 1
    Я читал ваши точки 1 и 2. Тогда я заметил, что в реальной арифметике (расширенной, чтобы позволить NaN в первую очередь) NaN равно себе - просто потому, что в математике любая сущность равна себе без исключения. Теперь я смущен: почему IEEE не «соответствует поведению реальной арифметики», что сделает NaN == NaN? Что мне не хватает? – max 8 April 2012 в 02:39
  • 2
    Согласовано; нерефлексивность NaNs не создавала никакого конца боли для таких языков, как Python, с ее семантикой сдерживания на основе равенства. Вы действительно не хотите, чтобы равенство не соответствовало отношению эквивалентности, когда вы пытаетесь создать контейнеры поверх него. И наличие двух отдельных понятий равенства - это не очень дружелюбный вариант, поскольку язык, который должен быть легко освоен. Результат (в случае Python) является неприятно хрупким компромиссом между уважением к IEEE 754 и не слишком-чередующейся семантикой сдерживания. К счастью, редко можно вводить NaN в контейнеры. – Mark Dickinson 15 April 2012 в 16:06
  • 3
    Некоторые замечательные наблюдения здесь: bertrandmeyer.com/2010/02/06/… – Mark Dickinson 15 April 2012 в 16:09
  • 4
    @StephenCanon: каким образом (0/0) == (+ INF) + (-INF) будет более бессмысленным, чем 1f/3f == 10000001f/30000002f? Если значения с плавающей запятой считаются классами эквивалентности, то a=b не означает «Вычисления, которые дали a и b, если они выполнялись с бесконечной точностью, дали бы идентичные результаты», а скорее « То, что известно о a, совпадает с тем, что известно о b ". Мне любопытно, знаете ли вы какие-либо примеры кода, где есть «Nan! = NaN». делает вещи проще, чем в противном случае? – supercat 21 August 2013 в 17:34
  • 5
    @StephenCanon: Это правда, что математика с плавающей запятой не может вести себя как группа, а тем более кольцо или поле, но это не означает, что числа с плавающей запятой не должны определять отношение эквивалентности. Я понимаю, что IEEE рекомендовал набор транзитивных реляционных операторов в 2007 году, но обнаружил, что недоумеваю, почему проблема, которая должна была стать очевидной в первый раз, когда кто-то пытался сортировать список номеров, содержащих NaN, по-прежнему требует сложного многоступенчатого обходной путь. Сортировочный код часто является одним из наиболее чувствительных к производительности приложений для сравнения с плавающей запятой ... – supercat 15 May 2014 в 18:20

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

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

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

42
ответ дан brainjam 21 August 2018 в 06:58
поделиться
  • 1
    +1 для приведения sqrt(a) = sqrt(b) <=> a = b – Bergi 2 April 2013 в 13:58
  • 2
    Каким должен быть результат sqrt (1.00000000000000022) == sqrt (1.0)? Как насчет (1E308 + 1E308-1E308-1E308-1E308) == (1E308 + 1E308)? Кроме того, только пять из шести сравнений возвращают false. Оператор != возвращает true. Если возвращать false NaN==NaN и NaN!=NaN, то код, который сравнивает x и y, будет выбирать, что должно происходить, когда оба операнда являются NaN, выбирая либо ==, либо !=. – supercat 10 October 2013 в 03:10

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

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

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

0
ответ дан Daren Thomas 21 August 2018 в 06:58
поделиться
  • 1
    «Это не число, и поэтому сравнение этого действительно не имеет смысла». Строки - это не цифры, но их сравнение имеет смысл. – jason 15 October 2009 в 18:02
  • 2
    да, сравнение строки с строкой имеет смысл. Но сравнение строки с, скажем, яблоками, не имеет большого смысла. Поскольку яблоки и груши не являются цифрами, имеет ли смысл сравнивать их? Что больше? – Daren Thomas 16 October 2009 в 10:24
  • 3
    @ DarenThomas: В SQL ни один «IF NULL = NULL THEN FOO» & quot; ни "IF Null & lt; & gt; Null THEN CALL FOO; & quot; [или независимо от того, какой синтаксис] выполнит FOO. Для того чтобы NaN быть эквивалентным if (NaN != NaN) foo();, не следует выполнять foo, но это так. – supercat 10 October 2013 в 03:15

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

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

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

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

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

0
ответ дан Ivan Castellanos 21 August 2018 в 06:58
поделиться
  • 1
    Но с NaN даже один и тот же «экземпляр» сравнивает неравные с самим собой. Если вы запустите x = NaN; x === x, вы получите false. Это отличается от Error: x = new Error; x === x, который дает вам true! – dpercy 3 October 2017 в 03:51

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

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

29
ответ дан Jack Ryan 21 August 2018 в 06:58
поделиться
  • 1
    Все пустые множества равны по определению. – MSalters 14 October 2009 в 10:51
  • 2
    Ящики, которые вы указали, НЕ известны как пустые. – John Smith 15 October 2009 в 17:38
  • 3
    Не могли бы вы сказать мне, что ящики не содержат одно и то же? Я могу понять обоснование для (NaN==Nan)==false. То, что я не понимаю, является обоснованием для (Nan!=Nan)==true. – supercat 10 February 2014 в 22:19
  • 4
    Я предполагаю, что NaN! = NaN истинно, потому что x! = Y определяется как! (X == y). Конечно, я не знаю, определяет ли спецификация IEEE таким образом. – Kef Schecter 26 March 2014 в 08:28
  • 5
    Но в этой аналогии, если вы дали мне коробку, сказали, что она не содержит яблок, а затем спросила меня, соответствует ли она самому себе, вы ожидаете, что я скажу «нет»? Потому что это то, что я должен сказать в соответствии с IEEE. – semicolon 30 March 2016 в 01:19

Это выглядит только странно, потому что большинство программных сред, которые позволяют 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 21 August 2018 в 06:58
поделиться
  • 1
    @DorianNiemiec: ваше редактирование разрушает точку ответа. – Mark Dickinson 19 April 2018 в 17:49

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

0
ответ дан Patrick Campbell 21 August 2018 в 06:58
поделиться

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

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

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

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

-2
ответ дан pushkin 21 August 2018 в 06:58
поделиться
  • 1
    Что вы имеете в виду & quot; это может быть любая ценность, кроме числа & quot ;? – pushkin 19 April 2018 в 19:47

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

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

4
ответ дан Rick Regan 21 August 2018 в 06:58
поделиться

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

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

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

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

-1
ответ дан sanaris 21 August 2018 в 06:58
поделиться

Очень короткий ответ:

Потому что следующее: nan / nan = 1 НЕ должно быть. В противном случае inf/inf будет 1.

(Следовательно, nan не может быть nan. Что касается > или <, если nan будет уважать любое отношение порядка в a множество, удовлетворяющее архимедовому свойству, мы бы снова nan / nan = 1 находились на пределе).

-2
ответ дан SeF 21 August 2018 в 06:58
поделиться
  • 1
    Нет, это не имеет смысла. У нас есть inf = inf и inf / inf = nan, поэтому nan = nan не будет препятствовать nan / nan = nan. – starblue 4 June 2018 в 09:46
  • 2
    @starblue Вы имеете в виду nan / nan = 1? В любом случае ... Ваши рассуждения имеют смысл, если inf и nan были такими же, как и любые другие числа. Это не так. Причина, по которой inf/inf должна быть nan (или неопределенной формой в математике), а не 1, является более тонкой, чем простая алгебраическая манипуляция (см. Теорему Де L'Госпиталя). – SeF 7 June 2018 в 14:55

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

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

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

12
ответ дан Stefan Rusek 21 August 2018 в 06:58
поделиться
  • 1
    Более того, даже если бы вы знали, какая операция, это не поможет. Я могу построить любое число формул, которые идут в 0/0 в некоторой точке, которые имеют (если мы предполагаем непрерывность) четко определенные и разные значения в этой точке. – David Thornley 15 October 2009 в 18:11
Другие вопросы по тегам:

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