Как отличить разные типы 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