1

这个问题来自为什么 isnormal() 说一个值是正常的而不是正常的?

AC 编译器生成以下代码,用于检测传入的 32 位float是否正常:

    flds    24(%esp)
    fxam; fstsw %ax;
    andw    $17664, %ax
    cmpw    $1024, %ax
    sete    %al

(完整代码可以在这里查看)。

这段代码正确吗?该程序似乎表现不正确,说一个数字是正常的,但不是。我们认为这里可能正在检查该数字的双精度正态性。

4

1 回答 1

1

我检查了英特尔的insn 参考手册,链接自https://stackoverflow.com/tags/x86/info

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

据此flds它本身会引发一个 Denormal 异常。我认为这意味着它正在测试实际源,而不是转换为 80 位的结果。该页面说异常异常将设置状态字中的位。

英特尔的参考手册没有说明fld设置状态字的任何内容,只是关于设置 C1 标志以及未定义 C0、C2 和 C3 的内容。它确实说如果源是非正常的,您可以获得#D FPU 异常,但如果源是 80 位格式,则不会发生这种情况。

如果未启用 FPU 异常,我不知道状态字是否真的会被设置为非规范化。我不是这方面的专家。我对本页(和控制字部分)的阅读是 FPU 状态字在大多数指令之后都会更新。如果该D位在控制寄存器中设置(默认情况下),则非正规操作数在状态字D中设置该位。如果它未设置(未屏蔽),则会发生异常。

所以我认为一个测试非正规浮点数的函数看起来像:

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。

我没有看到可以检查floatSSE 寄存器中的 a 是否异常的指令。

我想我有一种(缓慢的)方法来测试 SSE4.1 或 AVX 的非规范化ROUNDSS。您需要根据输入的符号使用不同的版本。

对于正值:

  • 用非正规数朝四舍五入+inf- 为零
  • +inf没有非正规数的方向舍入为零。
  • 如果两个舍入结果不同,则 denormals-are-zero 会产生影响(意味着输入是 denormal)

负数需要向-inf, not进行四舍五入+inf,否则-0.xx将始终四舍五入为零。所以这会有一个分支、两个ROUNDSSes 和一个比较。IEEE 浮点格式的 Bit-hacks 可能会更快。

于 2015-07-31T04:53:23.703 回答