Почему NaN поплавки?

В Python, если вы хотите создать NaN, это должно быть число с плавающей запятой, созданное как float("nan") (например). NaN должны быть числами с плавающей точкой и в других языках программирования (таких как C++ или Java).

Однако иногда имеет смысл иметь NaN в «целочисленных массивах».

Почему не существует «целого числа NaN»?

Я ищу документированный ответ на программные ограничения, объясняющие этот шаблон.

Я знаю, что PEP - это стандарты Python. Я ищу объяснение в истории языка или программных ограничений, которое оправдывает отсутствие целочисленного NaN, которое существует в других языках.

Clef. 10.11.2022 19:10

и да, если вы готовы использовать dtypes панд, панды представили целочисленный тип, допускающий значение NULL (тип Int в отличие от int), задокументированный здесь: pandas.pydata.org/docs/user_guide/integer_na.html

Michael Delgado 10.11.2022 19:10

Я отредактировал свой вопрос, чтобы сделать его более понятным. Я нахожу сомнительным думать, что выбор был сделан «из ниоткуда» разработчиками Python и просто так получилось, поэтому я хотел бы узнать, есть ли у кого-нибудь дополнительная документация по этой теме.

Clef. 10.11.2022 19:13

Мне трудно обдумать идею о том, что разработчики Python сделали явный выбор, чтобы не полностью отличаться от других языков. И я полагаю, что None существует с самого начала Python?

Dan Getz 10.11.2022 19:16

Подождите, вы говорите, что целые числа nan являются нормальными в других языках? Что это за языки?!

Dan Getz 10.11.2022 19:18

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

Michael Delgado 10.11.2022 19:19

Интересно — меня ввел в заблуждение разговор с коллегой о Java (о которой я ничего не знаю) и int NaNs в ней (которых вроде как на самом деле не существует). Я отредактирую этот вопрос, чтобы сделать его более общим, поскольку он определенно не специфичен для Python.

Clef. 10.11.2022 19:22

@Ключ. а как насчет ответа chrslg, который не касается вашего вопроса? это, я думаю, настолько близко к тому, что вы ищете, насколько вы собираетесь получить. Я не думаю, что вы найдете серьезно продуманное предложение удвоить требования к памяти для каждого целого числа, и вы также не найдете соображение «давайте просто отбросим число 255 навсегда».

Michael Delgado 10.11.2022 19:25

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

Rohit Gupta 11.11.2022 06:02

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

Sam Mason 11.11.2022 13:53
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
11
83
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Поскольку все целые числа непротиворечивы, они имеют значение.

8-битные целые числа без знака идут от 00000000 до 11111111, и все эти значения имеют четкое, определенное значение, то есть от 0 до 255.

Каким из этих значений вы бы пожертвовали, чтобы закодировать 8-битное целое число без знака NaN? 255 может быть. Но тогда вы жертвуете множеством возможных приложений. Вы не можете использовать 8-битное целое число без знака для обработки байтов (поскольку они находятся в [0,256)). Не используйте их для управления изображениями (белые (255 255 255) пикселей будут (NaN, NaN, NaN)). И т.п.

Не говоря уже о всей оптимизации, которая становится невозможной, если 11111111 беззнаковое 8-битное целое означает NaN, а 11111111 подписанное целое число означает -1 (что и должно быть сделано)

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

Потому что это еще одна причина, почему это не так: зачем это кому-то нужно? Я имею в виду, что NaN означает «Не число». То есть это означает, что биты, хранящиеся в памяти, где должно быть число, в действительности не представляют числа.

Для поплавков иначе. Потому что классическое кодирование чисел с плавающей запятой IEEE делает некоторую комбинацию битов бессмысленной (или специальной). Немного. Не то чтобы это был 1 неиспользованный бит или что-то отдаленно похожее. Всего несколько невозможных значений среди миллиардов возможных. Но все же некоторые комбинации битов недействительны. Или, точнее, мы придавали им какое-то особое значение, в том числе NaN.

Что касается int, вы сами решаете, что для вашего приложения вы можете пожертвовать значением. Например, если вы храните результаты игры в кости, у вас более чем достаточно выбора, чтобы решить, какое значение (-1, 0, 7, 99, ... любое, кроме 1, 2, 3, 4, 5 или 6) будет иметь значение. особое значение для вас (например, «кости не были брошены»). Система не может взять на себя ответственность пожертвовать какой-то комбинацией битов, то есть некоторыми возможными значениями, чтобы сделать их особенными, даже для тех, кому не нужно такое особое значение.

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

это как хорошо продуманный ответ, который прямо говорит о намерениях ОП, так и хорошая демонстрация того, почему это следует закрыть. хотя здесь есть хорошие аргументы в пользу компромиссов (+1 от меня за то, что я сэкономил время, чтобы написать все это в комментариях), ничто иное, как взвешивание Гвидо, не дало бы окончательного ответа на вопрос «почему python не делает этого?»

Michael Delgado 10.11.2022 19:22

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

Clef. 10.11.2022 19:28

@МайклДельгадо. Спасибо. Я бы добавил, что у Гвидо на самом деле не было своего мнения по этому поводу. Это было больше решение фон Неймана. А на самом деле даже он толком не решил, потому что это было очевидно. Опять же, как использовать те же 8 бит для кодирования 255 и -1. Не просто аналогия. Беззнаковое NaN должно быть 255. У нас не может быть пробела в возможном значении для целых чисел. NaN со знаком должен быть либо -128, либо +127 по той же причине. Желательно -128, для симметрии. Таким образом, любой подписанный NaN должен быть представлен иначе, чем неподписанный NaN. Или миру пришлось бы отказаться от -1≡255 вещи.

chrslg 10.11.2022 19:29

А без этого АЛУ потребовалось бы в два раза больше транзисторов. Я, конечно, преувеличиваю, так как ALU, особенно в наши дни, делают больше, чем ADD, SUB или MUL. Но эти операции должны существовать в двух версиях: одна для подписанных, другая для неподписанных. Потому что мы больше не могли бы использовать дополнение 2, если бы у нас было целое число NaN. И это только одна из проблем.

chrslg 10.11.2022 19:31

Этот ответ преувеличивает случай. С восьмибитными целыми числами может быть сложно пожертвовать одним значением для NaN, но вопрос касается целых типов в целом, а не только восьмибитных целых чисел. С 16- или 32-битными целыми числами это не такая жертва. А использование 1000000000000000 для NaN внесет симметрию в представляемые значения в 32-разрядном дополнении до двух со знаком. Как говорится в ответе Чукса, это скорее вопрос опробованного и потерянного на рынке. И неясно, что с современным оборудованием целочисленные NaN не будут выгодны и смогут занять место на рынке, если попытаются снова.

Eric Postpischil 10.11.2022 22:42

Re «То есть это означает, что биты, хранящиеся в месте памяти, где должно быть число, на самом деле не представляют число»: то же самое для целых типов типов с плавающей запятой, поэтому это не отвечает, почему NaN используются в типах с плавающей запятой, но не в целочисленных типах.

Eric Postpischil 10.11.2022 22:42

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

Eric Postpischil 10.11.2022 22:44

@EricPostpischil Цитируемая часть моего комментария не означает, что это должно быть «аппаратное обеспечение» (моё использование слова «система» здесь было довольно общим, а не о сложном). Просто если есть 8 бит NaN, то есть только 254 значения, отличные от nan. И что было бы странно, если бы «система» (жесткая, Python, numpy, что угодно) навязывала это всем в интересах очень немногих. Это все, что я сказал в процитированном предложении (позже я прокомментирую рассмотрение аппаратного обеспечения, но не здесь).

chrslg 10.11.2022 23:27

Что касается того, что железо поддерживает как подписанные, так и беззнаковые типы, то да, действительно. Но между ними было бы гораздо меньше факторизации, если бы из-за NaN нам пришлось отказаться от дополнения 2. Есть причина, по которой дополнение 2 стало универсальным. Кроме того, в настоящее время наличие двух дополнительных устройств может показаться не таким уж большим. Но когда-то это было очень важно, и действительно имело значение, что 200+10=210 и -56+10=-46 были одной и той же операцией.

chrslg 10.11.2022 23:33

@chrslg: неважно, аппаратное это или система; тот факт, что целочисленный тип с NaN поддерживается, не означает, что целочисленный тип той же ширины без NaN не может поддерживаться.

Eric Postpischil 11.11.2022 01:33

Почему не существует «целого числа NaN»?

Не стоит. Форматы дарвиновские - выживают только лучшие. Обратите внимание на потерю дополнения, величины знака, дополненных беззнаковых типов, не-8,16,32,64,... ширины.

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

Обратите внимание, что дополнение умирающих и целочисленное кодирование со знаком могут иметь -0 или «представление ловушки или нормальное значение» в C. Эта ловушка может повлиять на целочисленное NAN, но ни одна душа не просит об этом.

Другие вопросы по теме