简体   繁体   English

整数除以零的平台会触发浮点异常?

[英]On which platforms does integer divide by zero trigger a floating point exception?

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. 在另一个问题中,有人想知道他们为什么会得到一个“浮点错误”,实际上他们的C ++程序中有一个整数除零。 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: 这听起来很奇怪,因为我知道:

  1. 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) 所有Windows平台上x86和x64上的MSVC编译代码报告int除以零为“0xc0000094:整数除以零”,浮点除以零为0xC000008E“浮点除以零”(启用时)

  2. 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). IA-32和AMD64 ISA将#DE (整数除法异常)指定为中断0.浮点异常触发中断16(x87浮点)或中断19(SIMD浮点)。

  3. Other hardware have similarly different interrupts ( eg PPC raises 0x7000 on float-div-by-zero and doesn't trap for int/0 at all). 其他硬件具有类似的不同中断( 例如, PPC在float-div-by上引发0x7000并且根本不捕获int / 0)。

  4. 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. 我们的应用程序使用_controlfp_s内部函数(最终是stmxcsr op)取消屏蔽零除零的浮点异常,然后捕获它们以进行调试。 So I have definitely seen IEEE754 divide-by-zero exceptions in practice. 所以我在实践中肯定会看到IEEE754被零除的异常。

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) . 因此,我得出结论,有些平台将异常作为浮点异常报告,例如x64 Linux(无论ALU管道如何,都会针对所有算术错误提升SIGFPE)

What other operating systems (or C/C++ runtimes if you are the operating system) report integer div-by-zero as a floating point exception? 其他操作系统(如果您操作系统,还是C / C ++运行时)报告整数除零作为浮点异常?

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. 我不确定目前的情况如何,但目前的情况是FP异常检测支持与整数非常不同。 It's common for integer division to trap. 陷阱的整数除法很常见。 POSIX requires it to raise SIGFPE if it raises an exception at all. POSIX要求它在引发异常时引发SIGFPE

However, you can sort out what kind of SIGFPE it was, to see that it was actually a division exception. 但是,你可以理清它是什么类型的SIGFPE,看它实际上是一个除法异常。 (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 .) (但不一定是被0除以:2的补码INT_MIN / -1除法陷阱,当64b / 32b除法的商不适合32b输出寄存器时, x86的dividiv也陷阱。但事实并非如此在AArch64上使用sdiv 。)

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. glibc手册解释了BSD和GNU系统为SIGFPE的信号处理程序提供了额外的arg,它将被FPE_INTDIV_TRAP除以零。 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. siginfo_t包含该成员的系统上,POSIX将FPE_INTDIV_TRAP记录为siginfo_tint si_code字段的可能值。

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. IDK,如果Windows首先提供不同的异常,或者它将事物捆绑到与Unix相同的算术异常的不同风格中。 If so, the default handler decodes the extra info to tell you what kind of exception it was. 如果是这样,默认处理程序会对额外信息进行解码,以告诉您它是什么类型的异常。

POSIX and Windows both use the phrase "division by zero" to cover all integer division exceptions, so apparently this is common shorthand. POSIX和Windows都使用短语“除以零”来涵盖所有整数除法异常,所以显然这是常见的简写。 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. 对于知道关于INT_MIN / -1(带有2的补码)的人来说是一个问题,短语“除以零”可以被视为除法异常的同义词。 The phrase immediately points out the common case for people that don't know why integer division might be a problem. 这句话立即指出了那些不知道为什么整数除法可能成为问题的人的常见情况。


FP exceptions semantics FP异常语义

FP exceptions are masked by default for user-space processes in most operating systems / C ABIs. 对于大多数操作系统/ C ABI中的用户空间进程,FP异常在默认情况下被屏蔽。

This makes sense, because IEEE floating point can represent infinities, and has NaN to propagate the error to all future calculations using the value. 这是有道理的,因为IEEE浮点可以表示无穷大,并且有NaN将错误传播到使用该值的所有未来计算。

  • 0.0/0.0 => NaN 0.0/0.0 => NaN
  • If x is finite: x/0.0 => +/-Inf with the sign of x 如果x是有限的: x/0.0 => +/-Inf ,符号为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 vs. integer error detection FP与整数错误检测

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. FP检测错误的方法非常好:当屏蔽异常时,它们在FP状态寄存器中设置一个标志而不是陷阱。 (eg x86's MXCSR for SSE instructions). (例如x86的MXCSR用于SSE指令)。 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 (eg in an address calculation, you should trap instead of potentially storing to a bogus location). 允许屏蔽整数除法异常在某些情况下会很好,但在其他情况下会很危险(例如,在地址计算中,您应该陷阱而不是潜在地存储到虚假位置)。

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. 但是,在x86上,检测在计算序列期间是否发生整数溢出需要在每一个之后放置一个条件分支,因为标志只是被覆盖。 MIPS has an add instruction that will trap on signed overflow, and an unsigned instruction that never traps. MIPS有一个add指令,它将捕获有符号溢出,以及一个永不陷阱的无符号指令。 So integer exception detection and handling is a lot less standardized. 因此,整数异常检测和处理的标准化程度要低得多。


Integer division doesn't have the option of producing NaN or Inf results, so it makes sense for it to work this way . 整数除法不能选择产生NaN或Inf结果, 因此以这种方式工作是有意义的

Any integer bit pattern produced by integer division will be wrong, because it will represent a specific finite value. 整数除法产生的任何整数位模式都是错误的,因为它将代表一个特定的有限值。

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. 但是,在x86上,如果屏蔽了“浮点无效”异常,则使用cvtsd2si或任何类似的转换指令将超出范围的浮点值转换为整数会产生“整数不定”值。 The value is all-zero except the sign bit. 除符号位外,该值为全零。 ie INT_MIN . INT_MIN

(See the Intel manuals, links in the tag wiki. (参见英特尔手册, 标签wiki中的链接。

What other operating systems (or C/C++ runtimes if you are the operating system) report integer div-by-zero as a floating point exception? 其他操作系统(如果您是操作系统,还是C / C ++运行时)报告整数除零作为浮点异常?

The answer depends on whether you are in kernel space or user space . 答案取决于您是在内核空间还是用户空间 If you are in kernel space, you can put "i / 0" in kernel_main() , have your interrupt handler call an exception handler and halt your kernel. 如果你在内核空间,你可以将“i / 0”放在kernel_main() ,让你的中断处理程序调用异常处理程序并暂停你的内核。 If you're in user space, the answer depends on your operating system and compiler settings. 如果您在用户空间中,答案取决于您的操作系统和编译器设置。

AMD64 hardware specifies integer divide by zero as interrupt 0, different from interrupt 16 (x87 floating-point exception) and interrupt 19 (SIMD floating-point exception). AMD64硬件指定整数除零作为中断0,不同于中断16(x87浮点异常)和中断19(SIMD浮点异常)。

The "Divide-by-zero" exception is for dividing by zero with the div instruction. “除零”例外是用div指令除以零。 Discussing the x87 FPU is outside the scope of this question. 讨论x87 FPU超出了本问题的范围。

Other hardware have similarly different interrupts (eg PPC raises 0x7000 on float-div-by-zero and doesn't trap for int/0 at all). 其他硬件具有类似的不同中断(例如,PPC在float-div-by上引发0x7000并且根本不捕获int / 0)。

More specifically, 00700 is mapped to exception type "Program", which includes a floating-point enabled exception. 更具体地说, 00700映射到异常类型“Program”,其中包括一个启用浮点的异常。 Such an exception is raised if trying to divide-by-zero using a floating point instruction. 如果尝试使用浮点指令进行除零,则会引发此类异常。

On the other hand, integer division is undefined behavior per the PPC PEM: 另一方面,整数除法是每个PPC PEM的未定义行为:

8-53 divw 8-53分

If an attempt is made to perform either of the divisions—0x8000_0000 ÷ –1 or ÷ 0, then the contents of rD are undefined, as are the contents of the LT, GT, and EQ bits of the CR0 field (if Rc = 1). 如果尝试执行任一分区-0x8000_0000÷-1或÷0,则rD的内容未定义,CR0字段的LT,GT和EQ位的内容也是如此(如果Rc = 1 )。 In this case, if OE = 1 then OV is set. 在这种情况下,如果OE = 1则设置OV。

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. 我们的应用程序使用_controlfp_s内部函数(最终是stmxcsr op)取消屏蔽零除零的浮点异常,然后捕获它们以进行调试。 So I have definitely seen IEEE754 divide-by-zero exceptions in practice. 所以我在实践中肯定会看到IEEE754被零除的异常。

I think your time is better spent catching divide by zero at compile-time rather than at run-time. 我认为你的时间最好花在编译时而不是在运行时除以零。

For userspace, this happens on AIX running on POWER, HP-UX running on PA-RISC, Linux running on x86-64, macOS running on x86-64, Tru64 running on Alpha and Solaris running on SPARC. 对于用户空间,这发生在运行在POWER上的AIX上,在PA-RISC上运行的HP-UX,在x86-64上运行的Linux,在x86-64上运行的macOS,在Alpha上运行的Tru64和在SPARC上运行的Solaris上。

Avoiding divides-by-zero at compile time is much better. 在编译时避免除零会好得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM