如何区分Python中不同类型的NaN浮点数

12 投票
4 回答
5290 浏览
提问于 2025-04-16 05:11

我正在写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 NaNQuiet NaN,还是可能是Indeterminate呢?

有没有办法在Python中创建Signaling NaNQuiet NaNIndeterminate,以便与其他语言进行交互?(也许可以使用ctypes?)我想这可能是一个特定平台的解决方案,如果是这样我也能接受。

更新:在TestStand的序列编辑器中,我尝试创建两个变量,一个设置为NAN,另一个设置为IND。然后我把它保存到一个文件中。接着我打开这个文件,用Python读取每个变量。在这两种情况下,Python都把它们读取为nan浮点数。

4 个回答

2

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
5

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文件,里面的变量设置为INDNAN,然后从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)
9

我帮你查了一下,觉得你可以试试用 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平台上是这样。

撰写回答