fxam应该用于单精度浮点值吗?

时间:2015-07-31 02:57:34

标签: assembly x86 x87

这个问题来自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)。

这段代码是否正确?该程序似乎行为不正确,说不正常的数字是正常的。我们认为也许正在检查这个数字的双精度正态性。

1 个答案:

答案 0 :(得分:1)

我查看了来自insn reference manual的英特尔https://stackoverflow.com/tags/x86/info

fxam指令只有一个版本,它在80位寄存器上运行。所以是的,(低效率)测试80位临时值是否正常。 (效率更高的是test $1024, %eax,而不是屏蔽,然后是cmp。)

According to thisflds本身会引发一个非正常异常。我认为这意味着它会测试实际来源,而不是转换为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的非正规数。您需要使用不同的版本,具体取决于输入的符号。

对于正值:

  • 使用denormals-are-zero
  • +inf方向前进
  • +inf舍入而没有非正规零。
  • 如果两个舍入结果不同,则denormals-are-zero有效(意味着输入是非正规的)

负数需要舍入-inf,而不是+inf,否则-0.xx将始终舍入为零。所以这将有一个分支,两个ROUNDSS es和一个比较。 IEEE浮点格式的位攻击可能会更快。