C语言 isnan()和测试等式一样快吗?

dzhpxtsq  于 11个月前  发布在  其他
关注(0)|答案(4)|浏览(112)

在C语言中,测试一个浮点数是否为NaN和测试两个浮点数是否相等一样快吗?也就是说,isnan()和两个浮点数之间的简单相等性测试一样快吗?
我特别感兴趣的是在标准的现代英特尔/AMD平台上使用gcc
下面是一段C代码的示例。

#include <math.h>
int main(double x)
{
  return isnan(x);
}

字符串

mftmpeh8

mftmpeh81#

在x64上使用GCC,math.hisnan(float)编译为

jmp __isnanf

字符串
使用尾部调用优化,但有效地调用函数。被调用的函数将不得不做一些等效于代码的事情,至少我没有看到任何更快的方法来实现它。这留下了一个问题,它如何比较一个比较没有回答。
但这并没有说明“测试浮点数是否为NaN”有多快,因为并不只有一种方法可以做到这一点。最直接的方法,

int isnan2(float x)
{
  return x != x;
}


从字面上看,这与在C级别比较浮点数是一样的。但GCC使之:

xor eax, eax
ucomiss xmm0, xmm0
setp    al
ret


这和比较两个浮点数不太一样,但很接近,事实上更快一点。测试相等意味着测试无序的情况,就像这里一样,但是还必须测试z标志,像这样(再次来自gcc)

xor eax, eax
mov edx, 1
ucomiss xmm0, xmm1
setp    al
cmovne  eax, edx
ret


额外的好处:使用<cmath>使isnan编译成与比较浮点数本身相同的东西,请参阅链接的问题了解原因。
Godbolt link for convenience
我现在看到你实际上有double,但这并没有改变任何性质。

aiazj4mn

aiazj4mn2#

对于GCC(可能还有Clang),优化isnan()函数调用的关键在于代码中是否存在NaN信号,以及在遇到NaN信号时是否会引发浮点异常。
GCC默认采用-fno-signaling-nans,因此isnan(x)可以安全地优化为(x != x),在使用SSE 2的x86上,它将转换为UCOMISS或UCOMISD指令。
设置-fsignaling-nans将禁用此类优化。
ISO/IEC TS 18661-1(已纳入即将推出的C23标准)阐明了isnan()的行为,即即使参数是一个信令NaN,该函数也不会抛出任何异常(这使得IEEE 754中的“信令”NaN并不总是信令)。
另一方面,当x是一个信号NaN时,(x != x)表达式 * 确实 * 抛出FE_INVALID异常(也称为“无效操作”)。
下面是一个快速比较表,列出了使用“quiet”NaN和“signaling”NaN的各种表达式的行为:

| isnan(x) | (x != x)  | (x >= x)   | isgreaterequal(x,x)
--------------+----------+-----------+------------+--------------------
-Wfloat-equal | no warn  | warn      | no warn    | no warn
--------------+----------+-----------+------------+--------------------
Finite number | false    | false     | true       | true
Infinity      | false    | false     | true       | true
NaN (quiet)   | true     | true      | FPE; false | false
SNaN          | true     | FPE; true | FPE; false | FPE; false

字符串
备注:

  • isnan()isgreaterequal()在技术上返回int类型的值,而不是bool。表中的“true”和“false”分别对应于非零值和0值。
  • 表中的“FPE”表示将引发浮点异常;特别是“Invalid Operation”异常。

如果您的应用程序代码根本不使用信令NaN(或者您只在调试时使用它们),那么您可以为“更便宜”的isnan定义一个宏,它不会发出对libm isnan()函数的调用:

#include <math.h>
#ifdef isgreaterequal
#define isnan_cheap(x) (!isgreaterequal(x, x))
#endif


由于isgreaterequal宏在C中是泛型类型,因此此自定义宏也是泛型类型。

p8ekf7hl

p8ekf7hl3#

问题是我们是否应该在代码中使用NaN以外的东西来表示未知值。
然后你应该比较isnan(x)x == some_constant。如果some_constant没有0或NAN的值,那么如果使用典型的FP表示,比较可以简单地进行位比较-很难在速度上击败它。
不过,NaN更地道。

ecbunoof

ecbunoof4#

不。等式测试是内联的,所以你要付出调用isnan()的函数调用开销的代价。但是等式不能在不使用IEEE的情况下使用,所以...

相关问题