Как различить разные типы NaN-плавающих в Python - PullRequest
10 голосов
/ 08 октября 2010

Я пишу код Python 2.6, который взаимодействует с NI TestStand 4.2 через COM в Windows.Я хочу сделать значение «NAN» для переменной, но если я передам его float('nan'), TestStand отобразит его как IND.

Очевидно, TestStand различает значения с плавающей запятой «IND» и «NAN»,Согласно справке TestStand :

  • IND соответствует Сигнализация NaN в Visual C ++, тогда как
  • NAN соответствует QuietNaN

Это означает, что float('nan') Python фактически является сигнальным NaN при прохождении через COM.Однако из того, что я читал о Сигнализация NaN , кажется, что Сигнализация NaN немного "экзотично", а Quiet NaN - ваш "обычный" NaN,Поэтому я сомневаюсь, что Python будет передавать Сигнальный NaN через COM. Как я могу узнать, передается ли Python float('nan') через COM как Сигнальный NaN или Тихий NaN , или, возможно, Неопределенный ?

Есть ли способ сделать Сигнализирующий NaN против QuietNaN или Неопределенный в Python при взаимодействии с другими языками?(Возможно, с использованием ctypes?) Я предполагаю, что это будет решение для конкретной платформы, и я бы согласился с этим в этом случае.

Обновление: В редакторе последовательностей TestStand япопытался создать две переменные, одну для NAN, а другую для IND.Затем я сохранил его в файл.Затем я открыл файл и прочитал каждую переменную, используя Python.В обоих случаях Python читает их как nan float.

Ответы [ 4 ]

7 голосов
/ 08 октября 2010

Я немного покопался для вас, и я думаю, что вы могли бы использовать модуль struct в сочетании с информацией на Сводные диаграммы Кевина . Они объясняют точные битовые комбинации, используемые для различных типов чисел с плавающей запятой IEEE 754.

Единственное, с чем вам, вероятно, придется быть осторожным, если я прочту разделы об этом IND -определенном значении, это то, что это значение имеет тенденцию вызывать какое-то прерывание с плавающей запятой при назначении непосредственно в коде C, вызывая это должно быть превращено в простой NaN. Что, в свою очередь, означало, что этим людям советовали делать такие вещи в ASM, а не в C, так как C абстрагировал эти вещи. Так как это не моя область, и я не уверен, в какой степени это значение будет мешать Питон, я решил упомянуть об этом, чтобы вы могли, по крайней мере, следить за любым таким странным поведением. (См. Принятый ответ для этого вопроса ).

>>> import struct

>>> struct.pack(">d", float('nan')).encode("hex_codec")
'fff8000000000000'

>>> import scipy
>>> struct.pack(">d", scipy.nan).encode("hex_codec")
'7ff8000000000000'

Ссылаясь на Сводные диаграммы Кевина , которые показывают, что float('nan') фактически технически является неопределенным значением, в то время как scipy.nan является тихим NaN.

Давайте попробуем создать Сигнальный NaN, а затем проверить это.

>>> try_signaling_nan = struct.unpack(">d", "\x7f\xf0\x00\x00\x00\x00\x00\x01")[0]
>>> struct.pack(">d", try_signaling_nan).encode("hex_codec")
'7ff8000000000001'

Нет, Сигнальный NaN преобразуется в Тихий NaN.

Теперь давайте попробуем создать тихий NaN напрямую, а затем проверить его.

>>> try_quiet_nan = struct.unpack(">d", "\x7f\xf8\x00\x00\x00\x00\x00\x00")[0]
>>> struct.pack(">d", try_quiet_nan).encode("hex_codec")
'7ff8000000000000'

Так вот, как сделать правильный тихий NaN, используя struct.unpack() - по крайней мере, на платформе Windows.

3 голосов
/ 11 октября 2010

CPython определение nan

Когда Python сообщает nan, откуда это взялось?

  • Результат расчета (значения для платформы?)
  • Py_NAN в исходном коде CPython C
    • определяется как (Py_HUGE_VAL * 0.)
      • Значение зависит от платформы
      • Py_HUGE_VAL, вероятно, определяется как HUGE_VAL - в нем есть примечание, что оно должно быть HUGE_VAL, за исключением платформ, где это не работает.
  • float('nan'), который определяется из Py_NAN в исходном коде CPython C.

Чтение Python и Pywin32 Исходный код

Я посмотрел исходный код C для pywin32, в частности win32com, который формирует слой перевода Python↔COM. Этот код:

  • принимает входной объект
  • вызывает PyNumber_Float(), чтобы преобразовать его в Python float (если его еще нет)
  • вызывает PyFloat_AsDouble(), чтобы преобразовать его в простое значение C double.
    • Это просто возвращает C double, непосредственно содержащийся в PyFloatObject member ob_fval.

Похоже, что я проследил NaN от интерфейса COM до простого типа C double, содержащего Py_NAN, что бы это ни было на платформе Windows.

Значение TestStand NAN

Теперь я попробовал это с NI TestStand. Сначала я попробовал:

quiet_nan = struct.unpack(">d", "\x7f\xf8\x00\x00\x00\x00\x00\x01")[0]
# Set the variable's value in TestStand
locals_prop_object.SetValNumber(var_name, 0, quiet_nan)

Но это все еще появилось в TestStand как IND. Затем я создал файл TestStand с переменными, равными IND и NAN, и прочитал значения из Python. Оказывается, что TestStand NAN имеет значение FFFF000000000001. Согласно Сводным диаграммам Кевина , что является отрицательным тихим NAN. IND TestStand имеет ожидаемое значение для Не определено , FFF8000000000000.

Успех

Итак, после всего этого мне удалось установить NAN в TestStand из Python:

# Make a NAN suitable for TestStand
teststand_nan = struct.unpack(">d", "\xff\xff\x00\x00\x00\x00\x00\x01")[0]
# Set the variable's value in TestStand
locals_prop_object.SetValNumber(var_name, 0, teststand_nan)
2 голосов
/ 08 октября 2010

У Джона Кука есть хороший пост на эту тему, который может быть полезен:

Обновление: это не сработает?

In [144]: import scipy

In [145]: scipy.nan
Out[145]: 1.#QNAN

In [146]: scipy.inf
Out[146]: 1.#INF

In [147]: scipy.inf * 0
Out[147]: -1.#IND
0 голосов
/ 08 октября 2018

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

В форматах хранения с плавающей запятой, соответствующих стандарту IEEE 754, NaN идентифицируются с помощью определенных предварительно определенных битовых комбинаций, уникальных для NaN. Знаковый бит не имеет значения . NaN двоичного формата представлены в виде экспоненциального поля, заполненного единицами (например, значениями бесконечности), и некоторым ненулевым числом в поле значимости (чтобы отличать их от значений бесконечности). Исходный стандарт IEEE 754 от 1985 года (IEEE 754-1985) только описывал двоичные форматы с плавающей запятой и не определял, как должно маркироваться состояние сигнализации / тишины. На практике самый значимый бит поля значимости и поля определяет, сигнализирует ли NaN или тихий ... В пересмотре 2008 года стандарта IEEE 754 (IEEE 754-2008) даются официальные рекомендации по кодированию состояния сигнализации / молчания. Для двоичных форматов старшим значащим битом поля valuesand должен быть флаг is_quiet. То есть этот бит ненулевой, если NaN тихий, и нулевой, если NaN сигнализирует .

Поскольку большинство реализаций соответствуют стандарту IEEE 754-2008, вам следует следовать этому соглашению. В общем случае вы не можете планировать согласованность бита знака для NaN, даже для разных NaN на одной платформе. В соответствии с этим соглашением float('nan') и scipy.nan оба кажутся тихими NaN, по крайней мере в случаях, обсужденных выше.

...