我有一些数值代码,是在AMD64 Linux下使用LLVM 3.2开发的。
我最近将它移植到了带有XCode的OSX 10.9上。它可以正常运行,但它失败了很多单元测试:似乎有些计算在Linux上返回NaN(或-NaN),而现在在OSX上返回-NaN(或NaN)。
我是否可以安全地假定正NaN和负NaN是等价的,并调整我的单元测试以接受任何一种作为成功的标准,还是这是某些更严重问题的迹象?
我有一些数值代码,是在AMD64 Linux下使用LLVM 3.2开发的。
我最近将它移植到了带有XCode的OSX 10.9上。它可以正常运行,但它失败了很多单元测试:似乎有些计算在Linux上返回NaN(或-NaN),而现在在OSX上返回-NaN(或NaN)。
我是否可以安全地假定正NaN和负NaN是等价的,并调整我的单元测试以接受任何一种作为成功的标准,还是这是某些更严重问题的迹象?
printf
。在生成NaN之前,可能存在两个系统上符号不同的数值结果。测试程序未识别数值结果(非NaN结果)中的错误可能是因为测试不足。因此,值得调查为什么符号不同。 - Eric Postpischilif (isnan(x)) return x
,另一个平台有 if (isnan(x)) return NAN
或类似语句。唯一需要进一步调查的情况是,被测试的例程只包含基本算术运算(没有库函数调用),并且在完全相同的硬件上运行。 - Stephen Canon!((a < b) || (a > b))
,但这很棘手。 - supercat这完全取决于你的单元测试在测试什么。
除非你正在测试IEEE754浮点软件本身或打印它们的C运行时代码,否则大多数情况下你可以将它们视为相等的。否则,如果使用你正在测试的内容的代码将它们视为相同,则应将它们视为相同。
这是因为测试应该反映出你的真实用途,在每种情况下都如此。一个(虽然有些牵强)的例子是,如果你正在测试返回double的函数doCalc()
,并且它只被用于以下情况:
x = doCalc()
if x is any sort of Nan:
doSomethingWithNan()
如果您的测试应该将所有NaN
值视为等同,则可以这样使用它。但是,如果您像这样使用它:
x = doCalc()
if x is +Nan:
doSomethingForPositive()
else:
if x is -Nan:
doSomethingForNegative()
如果你希望将它们视为不同的内容,则需要进行区分。
同样地,如果你的实现在小数位上创建了有用的负载(请参见下文),并且你的真实代码使用了它,那么它也应该由单元测试来检查。
由于NaN只是指数中的所有1位和分数中除了所有零位之外的其他值,因此符号位可以是正或负,而分数位可以是各种各样的值。然而,它仍然是超出数据类型表示范围的一个值或结果,所以如果你期望得到这个值,那么符号或负载包含什么可能并没有太大的区别。
关于检查NaN值的文本输出,在NaN的维基百科页面中指出,不同的实现可能会给出非常不同的输出,其中包括:
nan
NaN
NaN%
NAN
NaNQ
NaNS
qNaN
sNaN
1.#SNAN
1.#QNAN
-1.#IND
甚至还有变体显示其NaN性质没有影响的符号和有效载荷:
-NaN
NaN12345
-sNaN12300
-NaN(s1234)
nan
。因此,对值进行不区分大小写的搜索以查找字符串nan
或ind
,就可以捕获它们。这可能在所有环境中都不起作用,但覆盖范围非常广。%f
(%F
使用大写字母)输出浮点数值有以下说明:
因此,简单地检查值是否包含表示
NaN
的double
参数会以[-]nan
或[-]nan(n-char-sequence)
其中一种方式转换,具体是哪种方式以及任何n-char-sequence
的含义都由实现定义。
nan
即可。printf
可以因多种原因显式地呈现它们:因为标准允许,并且该行为恰好从实现中自然派生出来,或者因为编写有关格式说明符的转换程序的人不知道没有理由区分它们,或者因为它们具有某些特定的用途,这与其需求相关,但超出了标准的范围。IEEE-754 NaN 的符号位没有语义意义。 - Stephen Canon
x
和/或y
是一个NaN,那么x == y
、x < y
和x > y
都会返回false;通过数值比较无法区分NaN。这表明你的测试要么检查代表NaN的位,要么检查NaN的某些转换,比如使用printf
打印NaN时产生的字符。在前一种情况下,有人已经决定了哪些位是重要的,你应该理解为什么。在后一种情况下,你依赖于printf
的实现相关属性。 - Eric Postpischil