Как отличить разные типы NaN float в Python

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

По-видимому, TestStand различает значения плавающей запятой "IND" и "NAN". Согласно Справка TestStand:

  • IND соответствует сигнальному NaN в Visual С++, а
  • NAN соответствует QuietNaN

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

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

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

Ответ 1

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

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

>>> import struct

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

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

Ссылаясь на Kevin Summary Charts, который показывает, что float('nan') на самом деле технически является неопределенным значением, тогда как scipy.nan является Quiet 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 преобразуется в Quiet NaN.

Теперь попробуйте сделать Quiet 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'

Итак, как сделать правильный Quiet NaN с помощью struct.unpack() - по крайней мере, на платформе Windows.

Ответ 2

Определение CPython для nan

Когда Python сообщает a 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 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 value in TestStand
locals_prop_object.SetValNumber(var_name, 0, quiet_nan)

Но это все еще появилось в TestStand как IND. Итак, я создал файл TestStand с переменными, установленными на IND и nan, и прочитал значения из Python. Оказывается, TestStand nan имеет значение FFFF000000000001. Согласно Kevin Summary Charts, это отрицательный тихий NAN. TestStand IND имеет ожидаемое значение для неопределенного, 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 value in TestStand
locals_prop_object.SetValNumber(var_name, 0, teststand_nan)

Ответ 3

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

Обновление: не будет ли это работать?

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