В Python, если вы хотите создать NaN, это должно быть число с плавающей запятой, созданное как float("nan")
(например). NaN должны быть числами с плавающей точкой и в других языках программирования (таких как C++ или Java).
Однако иногда имеет смысл иметь NaN в «целочисленных массивах».
Почему не существует «целого числа NaN»?
Я ищу документированный ответ на программные ограничения, объясняющие этот шаблон.
Я знаю, что PEP - это стандарты Python. Я ищу объяснение в истории языка или программных ограничений, которое оправдывает отсутствие целочисленного NaN, которое существует в других языках.
и да, если вы готовы использовать dtypes панд, панды представили целочисленный тип, допускающий значение NULL (тип Int
в отличие от int
), задокументированный здесь: pandas.pydata.org/docs/user_guide/integer_na.html
Я отредактировал свой вопрос, чтобы сделать его более понятным. Я нахожу сомнительным думать, что выбор был сделан «из ниоткуда» разработчиками Python и просто так получилось, поэтому я хотел бы узнать, есть ли у кого-нибудь дополнительная документация по этой теме.
Мне трудно обдумать идею о том, что разработчики Python сделали явный выбор, чтобы не полностью отличаться от других языков. И я полагаю, что None
существует с самого начала Python?
Подождите, вы говорите, что целые числа nan являются нормальными в других языках? Что это за языки?!
согласился - языки программирования, с которыми я знаком, следуют тем же стандартам, что и python, без целочисленных типов, допускающих значение NULL. на какой язык вы ссылаетесь?
Интересно — меня ввел в заблуждение разговор с коллегой о Java (о которой я ничего не знаю) и int NaNs в ней (которых вроде как на самом деле не существует). Я отредактирую этот вопрос, чтобы сделать его более общим, поскольку он определенно не специфичен для Python.
@Ключ. а как насчет ответа chrslg, который не касается вашего вопроса? это, я думаю, настолько близко к тому, что вы ищете, насколько вы собираетесь получить. Я не думаю, что вы найдете серьезно продуманное предложение удвоить требования к памяти для каждого целого числа, и вы также не найдете соображение «давайте просто отбросим число 255 навсегда».
Я проголосовал за то, чтобы не закрывать его, поскольку вопрос требует цитат, а не мнений.
Если вы работаете с Python, вы можете взглянуть на маскированные массивы, чтобы узнать, как включить пропущенные значения в целочисленные массивы.
Поскольку все целые числа непротиворечивы, они имеют значение.
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 не делает этого?»
Я не согласен с тем, что этот вопрос должен быть закрыт: этот ответ явно не основан на мнении, а детализирует (очень хорошо) программные ограничения, которые объясняют, почему происходит этот выбор.
@МайклДельгадо. Спасибо. Я бы добавил, что у Гвидо на самом деле не было своего мнения по этому поводу. Это было больше решение фон Неймана. А на самом деле даже он толком не решил, потому что это было очевидно. Опять же, как использовать те же 8 бит для кодирования 255 и -1. Не просто аналогия. Беззнаковое NaN должно быть 255. У нас не может быть пробела в возможном значении для целых чисел. NaN со знаком должен быть либо -128, либо +127 по той же причине. Желательно -128, для симметрии. Таким образом, любой подписанный NaN должен быть представлен иначе, чем неподписанный NaN. Или миру пришлось бы отказаться от -1≡255
вещи.
А без этого АЛУ потребовалось бы в два раза больше транзисторов. Я, конечно, преувеличиваю, так как ALU, особенно в наши дни, делают больше, чем ADD, SUB или MUL. Но эти операции должны существовать в двух версиях: одна для подписанных, другая для неподписанных. Потому что мы больше не могли бы использовать дополнение 2, если бы у нас было целое число NaN. И это только одна из проблем.
Этот ответ преувеличивает случай. С восьмибитными целыми числами может быть сложно пожертвовать одним значением для NaN, но вопрос касается целых типов в целом, а не только восьмибитных целых чисел. С 16- или 32-битными целыми числами это не такая жертва. А использование 1000000000000000 для NaN внесет симметрию в представляемые значения в 32-разрядном дополнении до двух со знаком. Как говорится в ответе Чукса, это скорее вопрос опробованного и потерянного на рынке. И неясно, что с современным оборудованием целочисленные NaN не будут выгодны и смогут занять место на рынке, если попытаются снова.
Re «То есть это означает, что биты, хранящиеся в месте памяти, где должно быть число, на самом деле не представляют число»: то же самое для целых типов типов с плавающей запятой, поэтому это не отвечает, почему NaN используются в типах с плавающей запятой, но не в целочисленных типах.
Относительно «Система не может взять на себя ответственность пожертвовать некоторой комбинацией битов, то есть некоторыми возможными значениями, чтобы сделать одно и то же особенным, даже для тех, кому не нужно такое специальное значение»: поддержка целочисленных типов с NaN не требует оборудование не поддерживает целые типы без NaN. Аппаратное обеспечение поддерживает оба типа со знаком, предлагая разные инструкции для разных типов, где это необходимо (например, умножение, переход по результату сравнения).
@EricPostpischil Цитируемая часть моего комментария не означает, что это должно быть «аппаратное обеспечение» (моё использование слова «система» здесь было довольно общим, а не о сложном). Просто если есть 8 бит NaN
, то есть только 254 значения, отличные от nan. И что было бы странно, если бы «система» (жесткая, Python, numpy, что угодно) навязывала это всем в интересах очень немногих. Это все, что я сказал в процитированном предложении (позже я прокомментирую рассмотрение аппаратного обеспечения, но не здесь).
Что касается того, что железо поддерживает как подписанные, так и беззнаковые типы, то да, действительно. Но между ними было бы гораздо меньше факторизации, если бы из-за NaN нам пришлось отказаться от дополнения 2. Есть причина, по которой дополнение 2 стало универсальным. Кроме того, в настоящее время наличие двух дополнительных устройств может показаться не таким уж большим. Но когда-то это было очень важно, и действительно имело значение, что 200+10=210
и -56+10=-46
были одной и той же операцией.
@chrslg: неважно, аппаратное это или система; тот факт, что целочисленный тип с NaN поддерживается, не означает, что целочисленный тип той же ширины без NaN не может поддерживаться.
Почему не существует «целого числа NaN»?
Не стоит. Форматы дарвиновские - выживают только лучшие. Обратите внимание на потерю дополнения, величины знака, дополненных беззнаковых типов, не-8,16,32,64,... ширины.
Существует скудная потребность в целочисленном типе NAN, и языки отражают это.
Обратите внимание, что дополнение умирающих и целочисленное кодирование со знаком могут иметь -0 или «представление ловушки или нормальное значение» в C. Эта ловушка может повлиять на целочисленное NAN, но ни одна душа не просит об этом.
PEP - это стандарты Python, а не numpy или tensorflow. Если вы ищете «почему», это не тема на Stack Overflow, но этот NEP по отсутствующим данным , вероятно, то, что вы ищете. numpy берет определения типов из стандартов типов данных IEEE — см. другие документы numpy по специальным значениям с плавающей запятой и IEEE_754