【问题标题】:Should fxam work for single precision floating point values?fxam 应该适用于单精度浮点值吗?
【发布时间】:2015-07-31 02:57:33
【问题描述】:

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

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

【问题讨论】:

  • 关于我认为你可以认为它是正确的唯一论点是浮点算术无论如何都不符合给定的实现。这将是编译器使用大于所需精度执行浮点运算的另一种情况,从而生成不正确的结果。

标签: assembly x86 x87


【解决方案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 状态字在大多数指令之后都会更新。如果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。

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

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

对于正值:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-17
    • 2017-09-22
    • 2010-11-07
    • 1970-01-01
    • 2018-02-23
    • 2020-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多