在哪些平台上整数除以零会触发浮点异常?
在另一个问题中,有人想知道为什么他们会得到浮点错误",而实际上他们的 C++ 程序中有一个整数被零除.围绕这个问题展开了讨论,有些人断言实际上从来没有为 float 除以零引发浮点异常,而只会在 integer 除以零时出现.
In another question, someone was wondering why they were getting a "floating point error" when in fact they had an integer divide-by-zero in their C++ program. A discussion arose around this, with some asserting that floating point exceptions are in fact never raised for float divide by zero, but only arise on integer divide by zero.
这对我来说听起来很奇怪,因为我知道:
This sounds strange to me, because I know that:
所有 Windows 平台上 x86 和 x64 上的 MSVC 编译代码报告 int 除以零为0xc0000094:整数除以零",浮点除以零为 0xC000008E浮点除以零"(启用时)
MSVC-compiled code on x86 and x64 on all Windows platforms reports an int divide by zero as "0xc0000094: Integer division by zero", and float divide by zero as 0xC000008E "Floating-point division by zero" (when enabled)
IA-32 和 AMD64 ISA 指定 #DE
(整数除法异常) 作为中断 0.浮点异常触发中断 16(x87 浮点)或中断 19(SIMD 浮点).
IA-32 and AMD64 ISAs specify #DE
(integer divide exception) as interrupt 0. Floating-point exceptions trigger interrupt 16 (x87 floating-point) or interrupt 19 (SIMD floating-point).
其他硬件具有类似的不同中断(例如 PPC 在 float-div-by-zero 上引发 0x7000 并且根本不会捕获 int/0).
Other hardware have similarly different interrupts (eg PPC raises 0x7000 on float-div-by-zero and doesn't trap for int/0 at all).
我们的应用程序使用 _controlfp_s
内在函数(最终是 stmxcsr
操作)取消被零除的浮点异常,然后捕获它们以进行调试.所以我在实践中肯定见过 IEEE754 除零异常.
Our application unmasks floating-point exceptions for divide-by-zero with the _controlfp_s
intrinsic (ultimately stmxcsr
op) and then catches them for debugging purposes. So I have definitely seen IEEE754 divide-by-zero exceptions in practice.
所以我得出结论,有一些平台将int异常报告为浮点异常,例如x64 Linux(无论 ALU 管道如何,都为所有算术错误提高 SIGFPE).
So I conclude that there are some platforms that report int exceptions as floating point exceptions, such as x64 Linux (raising SIGFPE for all arithmetic errors regardless of ALU pipe).
还有哪些其他操作系统(或 C/C++ 运行时,如果您是操作系统)将整数 div-by-zero 报告为浮点异常?
What other operating systems (or C/C++ runtimes if you are the operating system) report integer div-by-zero as a floating point exception?
推荐答案
我不知道现在的情况是怎样的,但目前的情况是 FP 异常检测支持与整数非常不同.整数除法陷入困境是很常见的.POSIX 要求它提高 SIGFPE
如果它引发异常.
I'm not sure how the current situation came to be, but it's currently the case that FP exception detection support is very different from integer. It's common for integer division to trap. POSIX requires it to raise SIGFPE
if it raises an exception at all.
但是,你可以梳理一下它是什么类型的SIGFPE,看看它实际上是一个除法异常.(虽然不一定被零除:2 的补码 INT_MIN
/-1
除法陷阱,以及 x86 的 div
和 idiv
也会在 64b/32b 除法的商不适合32b 输出寄存器.但情况并非如此AArch64 使用 sdiv
.)
However, you can sort out what kind of SIGFPE it was, to see that it was actually a division exception. (Not necessarily divide-by-zero, though: 2's complement INT_MIN
/ -1
division traps, and x86's div
and idiv
also trap when the quotient of 64b/32b division doesn't fit in the 32b output register. But that's not the case on AArch64 using sdiv
.)
glibc 手册解释 BSD并且 GNU 系统为 SIGFPE
的信号处理程序提供了一个额外的 arg,这将是 FPE_INTDIV_TRAP
用于除以零.在 siginfo_t
包含该成员的系统上,POSIX 将 FPE_INTDIV_TRAP
记录为 siginfo_t
的 int si_code
字段的可能值.
The glibc manual explains that BSD and GNU systems deliver an extra arg to the signal handler for SIGFPE
, which will be FPE_INTDIV_TRAP
for divide by zero. POSIX documents FPE_INTDIV_TRAP
as a possible value for siginfo_t
's int si_code
field, on systems where siginfo_t
includes that member.
IDK 如果 Windows 首先提供不同的异常,或者它是否像 Unix 那样将事物捆绑到相同算术异常的不同风格中.如果是这样,默认处理程序会解码额外信息以告诉您它是哪种异常.
IDK if Windows delivers a different exception in the first place, or if it bundles things into different flavours of the same arithmetic exception like Unix does. If so, the default handler decodes the extra info to tell you what kind of exception it was.
POSIX 和 Windows 都使用短语被零除"来涵盖所有整数除法异常,因此显然这是常见的速记.对于确实知道 INT_MIN/-1(带 2 的补码)是一个问题的人,短语被零除"可以被视为除法异常的同义词.对于不知道为什么整数除法可能会成为问题的人来说,这句话立即指出了常见情况.
POSIX and Windows both use the phrase "division by zero" to cover all integer division exceptions, so apparently this is common shorthand. For people that do know about about INT_MIN / -1 (with 2's complement) being a problem, the phrase "division by zero" can be taken as synonymous with a divide exception. The phrase immediately points out the common case for people that don't know why integer division might be a problem.
默认情况下,大多数操作系统/C ABI 中的用户空间进程都会屏蔽 FP 异常.
FP exceptions are masked by default for user-space processes in most operating systems / C ABIs.
这是有道理的,因为 IEEE 浮点数可以表示无穷大,并且有 NaN 将错误传播到所有使用该值的未来计算.
This makes sense, because IEEE floating point can represent infinities, and has NaN to propagate the error to all future calculations using the value.
0.0/0.0
=>NaN
- 如果
x
是有限的:x/0.0
=>+/-Inf
带有 x 的符号
0.0/0.0
=>NaN
- If
x
is finite:x/0.0
=>+/-Inf
with the sign of x
当异常被屏蔽时,这甚至允许这样的事情产生合理的结果:
This even allows things like this to produce a sensible result when exceptions are masked:
double x = 0.0;
double y = 1.0/x; // y = +Inf
double z = 1.0/y; // z = 1/Inf = 0.0, no FP exception
<小时>
FP 与整数错误检测
FP 检测错误的方式非常好:当异常被屏蔽时,它们在 FP 状态寄存器中设置一个标志而不是捕获.(例如,用于 SSE 指令的 x86 的 MXCSR).该标志保持设置直到手动清除,因此您可以检查一次(例如在循环之后)以查看发生了哪些异常,但不知道它们发生的位置.
FP vs. integer error detection
The FP way of detecting errors is pretty good: when exceptions are masked, they set a flag in the FP status register instead of trapping. (e.g. x86's MXCSR for SSE instructions). The flag stays set until manually cleared, so you can check once (after a loop for example) to see which exceptions happened, but not where they happened.
已经有建议具有类似的粘性"整数-溢出标志 用于记录在一系列计算过程中的任何一点是否发生溢出.允许屏蔽整数除法异常在某些情况下会很好,但在其他情况下很危险(例如,在地址计算中,您应该捕获而不是潜在地存储到虚假位置).
There have been proposals for having similar "sticky" integer-overflow flags to record if overflow happened at any point during a sequence of computations. Allowing integer division exceptions to be masked would be nice in some cases, but dangerous in other cases (e.g. in an address calculation, you should trap instead of potentially storing to a bogus location).
然而,在 x86 上,检测在一系列计算期间是否发生整数溢出需要在每个计算之后放置一个条件分支,因为标志只是被覆盖.MIPS 有一个 add
指令,它会在有符号溢出时陷入困境,还有一个无符号指令,从不陷入困境.因此整数异常检测和处理的标准化程度要低得多.
On x86, though, detecting if integer overflow happened during a sequence of calculations requires putting a conditional branch after every one of them, because flags are just overwritten. MIPS has an add
instruction that will trap on signed overflow, and an unsigned instruction that never traps. So integer exception detection and handling is a lot less standardized.
整数除法没有产生 NaN 或 Inf 结果的选项,所以它以这种方式工作是有意义的.
Integer division doesn't have the option of producing NaN or Inf results, so it makes sense for it to work this way.
整数除法产生的任何整数位模式都是错误的,因为它将代表一个特定的有限值.
Any integer bit pattern produced by integer division will be wrong, because it will represent a specific finite value.
但是,在 x86 上,使用 cvtsd2si
或任何类似的转换指令 会产生整数不定"值.除符号位外,该值全为零.即 INT_MIN
.
However, on x86, converting an out-of-range floating point value to integer with cvtsd2si
or any similar conversion instruction produces the "integer indefinite" value if the "floating-point invalid" exception is masked. The value is all-zero except the sign bit. i.e. INT_MIN
.
(参见英特尔手册,x86 标记维基.
(See the Intel manuals, links in the x86 tag wiki.
相关文章