如何区分Python中不同类型的NaN浮点数
我正在写Python 2.6的代码,目的是通过COM在Windows上与NI TestStand 4.2进行交互。我想为一个变量创建一个"NAN"值,但如果我传递float('nan')
,TestStand会把它显示为IND
。
显然,TestStand区分浮点数中的"IND"和"NAN"值。根据TestStand的帮助文档:
IND
对应于Visual C++中的Signaling NaN,而NAN
对应于Quiet NaN
这意味着Python的float('nan')
在通过COM传递时实际上是一个Signaling NaN。不过,根据我对Signaling NaN的了解,它似乎有点“特殊”,而Quiet NaN才是你常见的"NAN"。所以我对Python是否会通过COM传递Signaling NaN有些怀疑。我该如何确认Python的float('nan')
在通过COM传递时是作为Signaling NaN、Quiet NaN,还是可能是Indeterminate呢?
有没有办法在Python中创建Signaling NaN、Quiet NaN或Indeterminate,以便与其他语言进行交互?(也许可以使用ctypes
?)我想这可能是一个特定平台的解决方案,如果是这样我也能接受。
更新:在TestStand的序列编辑器中,我尝试创建两个变量,一个设置为NAN
,另一个设置为IND
。然后我把它保存到一个文件中。接着我打开这个文件,用Python读取每个变量。在这两种情况下,Python都把它们读取为nan
浮点数。
4 个回答
John Cook写了一篇很不错的文章,可能对你有帮助:
更新: 这个方法不行吗?
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
CPython中nan的定义
当Python报告一个nan
时,这个值是从哪里来的呢?
- 可能是某个计算的结果(具体值可能因平台而异)
- 在CPython的C源代码中有
Py_NAN
- 它的定义是
(Py_HUGE_VAL * 0.)
- 这个值是和平台相关的
Py_HUGE_VAL
可能是定义为HUGE_VAL
,有说明说在某些平台上这个定义可能会出问题。
- 它的定义是
- 通过
float('nan')
来创建的,这个也是在CPython的C源代码中定义的Py_NAN
。
阅读Python和pywin32的源代码
我查看了pywin32
的C源代码,特别是win32com
,它是Python和COM之间的转换层。这个代码:
- 接收输入对象
- 调用
PyNumber_Float()
将其转换为Python的float
(如果它还不是的话) - 调用
PyFloat_AsDouble()
将其转换为普通的C语言double
值。- 这个函数直接返回
PyFloatObject
中的ob_fval
成员所包含的C语言double
。
- 这个函数直接返回
所以看起来我已经追踪到了一个NaN
,从COM接口回溯到一个包含Py_NAN
的普通C语言double
类型,不管在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
。根据Kevin的总结图表,这是一个负的静默NAN。TestStand的IND
确实有预期的不确定值FFF8000000000000
。
成功
所以,经过这些尝试,我终于成功地在TestStand中从Python设置了一个NAN:
# 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)
我帮你查了一下,觉得你可以试试用 struct
模块,结合 Kevin的总结图表 上的信息。那些图表解释了各种类型的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的总结图表,float('nan')
实际上是技术上讲的Indeterminate值,而 scipy.nan
是Quiet NaN。
我们来试试创建一个Signaling 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'
不行,Signaling 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'
这就是如何使用 struct.unpack()
创建一个合适的Quiet NaN——至少在Windows平台上是这样。