这个问题来自Why is isnormal() saying a value is normal when it isn't?
C编译器生成以下代码,该代码用于检测传入的32位float
是否为“正常”:
flds 24(%esp)
fxam; fstsw %ax;
andw $17664, %ax
cmpw $1024, %ax
sete %al
(可以查看完整代码here)。
这段代码是否正确?该程序似乎行为不正确,说不正常的数字是正常的。我们认为也许正在检查这个数字的双精度正态性。
答案 0 :(得分:1)
我查看了来自insn reference manual的英特尔https://stackoverflow.com/tags/x86/info。
fxam
指令只有一个版本,它在80位寄存器上运行。所以是的,(低效率)测试80位临时值是否正常。 (效率更高的是test $1024, %eax
,而不是屏蔽,然后是cmp
。)
According to this,flds
本身会引发一个非正常异常。我认为这意味着它会测试实际来源,而不是转换为80位的结果。该页面表示非正规异常将设置状态字中的位。
英特尔的参考手册没有说明fld
设置状态字,只是关于设置C1标志,并且未定义C0,C2和C3。它确实说如果源是非正规的,你可以获得#D FPU异常,但是如果源是80位格式的话,这不会发生。
如果未启用FPU异常,我不知道状态字是否会实际设置为非正规。我不是这方面的专家。我对this page(以及控制字部分)的阅读是在大多数指令之后更新FPU状态字。如果在{strong>控制寄存器(默认情况下)中设置D
位,则非正规操作数将状态字中的D
位设置为。它没有设置(未屏蔽),会发生异常。
所以我认为测试一个非正规的float的函数看起来像:
isdenormalf:
flds (%rdi) # sets FPU status based on the input to the 32->80bit conversion
fstsw %ax
fstp %st0 # pop
test $2, %al # avoid 16 bit ops (%ax), they're slow on Intel
sete %al # or just branch on flags directly if your compiler's smart
ret
我还没试过这个,所以这可能完全是假的。以一种无需加载/弹出我们想要保持加载的数据进行内联的方式编写此文件可能并非易事。也许拿一个地址arg,返回一个浮点数(因此它可以在x87寄存器中),并且输出arg具有条件。
我没有看到可以检查SSE寄存器中float
是否正常的指令。
我认为我有一种(慢)方法来测试SSE4.1或AVX ROUNDSS
的非正规数。您需要使用不同的版本,具体取决于输入的符号。
对于正值:
+inf
方向前进
+inf
舍入而没有非正规零。负数需要舍入-inf
,而不是+inf
,否则-0.xx
将始终舍入为零。所以这将有一个分支,两个ROUNDSS
es和一个比较。 IEEE浮点格式的位攻击可能会更快。