为什么 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.
0x123
和 0x124
是非常接近 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
的情况下逃脱以排除无序情况。
请注意,生成掩码的压缩比较指令(如 cmppd
和 cmpsd
仍然在其比较谓词的名称中使用 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, 0
做 tmp += (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!=OF
或SF==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 比较通过与实际整数减法完全不同的机制设置进位标志。
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
, andCF
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.
0x123
和 0x124
是非常接近 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
自 8086fcomi
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
的情况下逃脱以排除无序情况。
请注意,生成掩码的压缩比较指令(如 cmppd
和 cmpsd
仍然在其比较谓词的名称中使用 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, 0
做 tmp += (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!=OF
或SF==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 比较通过与实际整数减法完全不同的机制设置进位标志。