为什么 x86 FP 像无符号整数一样比较集合 CF,而不是使用有符号条件?

Why do x86 FP compares set CF like unsigned integers, instead of using signed conditions?

COMISD 指令的英特尔指令参考中提供了以下文档:

Compares the double-precision floating-point values in the low quadwords of operand 1 (first operand) and operand 2 (second operand), and sets the ZF, PF, and CF flags in the EFLAGS register according to the result (unordered, greater than, less than, or equal).

CF的标志点在这里不是很清楚,因为它与无符号整数的算术运算有关。相比之下,文档关注的是按定义签名的浮点数。我 运行 做了一些实验,比如

mov rax, 0x123
movq xmm0, rax

mov rax, 0x124
movq xmm1, rax

ucomisd xmm0, xmm1 ;CF is set here like if
                    ;we would compare uints 0x123 and 0x124

所以指令在设置进位标志时将ope运行ds视为无符号整数,而ope运行ds是双精度浮点数?

对我来说它看起来有点 st运行ge。

您应该查看页面下方的 "Operation" 部分。总结:

  • PF无序时设置,有序时清空
  • ZF 如果为零或无序则设置,如果非零则清除
  • CF 如果小于或无序则设置,如果大于
  • 则清除

是的,位模式被解释为 IEEE 754 binary64 double-precision floating point numbers.

0x1230x124 是非常接近 0.0 的正次正规值的位模式。如果在 MXCSR 中设置了 DAZ,它将被解释为恰好 0.0。但默认情况下,次正规是按照 IEEE754 处理的。


如果您想测试它是否真的是 sign/magnitude FP 比较,而不是无符号整数比较,请使用负 FP 值进行测试。 (高位设置=>更高的无符号整数,但代表更负的更低FP值)。

有趣的事实:除了符号位,IEEE FP 使用的偏置指数表示法确实 使得比较 FP 数字的整数位模式成为可能。并将 nextafter 实现为位模式的整数增量,在检查符号以确定是否添加 +1-1.

之后

现代 x86 SSE/AVX 标量 FP 比较集合 EFLAGS 的方式与原始 8086 + 8087
fcom + fstsw ax 1 + sahf.

  • fcom 自 8086
  • fcomi PPro新增,直接设置EFLAGS
  • [u]comis[sd] SSE/SSE2 新增,也直接设置 EFLAGS。

排除"unordered"后,"above"(>)、"below"(<)、"equal"(==) jcc/setcc/cmovcc/fcmovcc 的条件都具有适当的语义。 (以及它们的组合,如 jae。)

保持标志设置相同使程序员和编译器开发人员更容易放入标量 SSE 代码,代替标量 x87 代码,而无需重做任何关于无序比较方式的逻辑(PF=ZF =CF=1) 会去。 ja (CF==0) 之类的技巧仅适用于 >(不适用于无序、相等或更低)在相同的分支上仍然有效。

请参阅 http://www.ray.masmcode.com/tutorial/fpuchap7.htm for x87 FP comparisons. Also related: x86 assembler: floating point compare 以了解有关标志设置的更多信息,以及您有时如何在没有 jp 的情况下逃脱以排除无序情况。

请注意,生成掩码的压缩比较指令(如 cmppdcmpsd 仍然在其比较谓词的名称中使用 lt for less than。 (自 AVX 以来,有更详细的谓词名称,如 LT_OQ(QNaN 也不例外)vs. LT_OS(QNaN 具有其通常的效果)vs. NLT_US(无序:也是 true当比较无序时)。由于它们必须从每个打包比较中产生 0/1 结果,因此那些 SIMD 比较指令需要一个谓词来检查以及只进行比较。


此外,无符号条件 (CF) 允许进行更多优化。因此,更改为已签名的条件会更糟。

x86 使用 CF 的指令多于任何其他标志 。例如,您可以用 ucomisd / adc eax, 0tmp += (x > 10)。如果 SSE/SSE2 已决定设置 SF(并清除 OF),则需要 sets 或其他 setcc 来提供 add 指令。


为什么 x87 使用 ja/jbe 而不是 jg/jle?

OF在FLAGS的低8位之外所以sahf不能设置。 popf 设置整个 FLAGS 寄存器可以设置或清除其他非条件 FLAGS,如 IF(启用中断)或 TF(每条指令后的单步陷阱)。再加上因为修改了SP一般使用起来不太方便

有符号标志条件是基于SF!=OFSF==OF,所以原来的8086 FP分支机制不可能使用有符号条件。相反,他们将 FP 状态字中的 C0、C2 和 C3 位与 FLAGS 中的 CF、PF 和 ZF 对齐。 This answer 有一个 ASCII 艺术图。


脚注 1:根据 NASM's appendix B,实际上 fstsw ax 是 286 中的新内容。在 actual 8086+8087 代码中,您可以使用类似 fstsw [bp-2] / mov ax, [bp-2] / sahf 或任何您想要的划痕 space使用。


So the instruction treats the operands as unsigned integers

不,绝对不是。它们被解释为 sign/magnitude IEEE binary64 FP 位模式。

无符号整数比较会对负浮点数给出不同的结果:高位设置 => 更高的无符号整数,但表示负 FP 值。

设置高位后,0x8...4 是高于 0x8...3 的无符号整数,但作为 FP 位模式,它表示更负(更低)的数字。

在将 "above" / "below" 条件用于 FP 时忘记 "unsigned" 关联。 这正是 x86 所称的测试进位标志的条件。

FP 比较通过与实际整数减法完全不同的机制设置进位标志。