在哪些平台上,整数除以零会触发浮点异常

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

本文关键字:异常 平台 整数      更新时间:2023-10-16

在另一个问题中,有人想知道,当他们的C++程序中有一个整数被零整除时,为什么会出现"浮点错误"。围绕这一点展开了讨论,一些人断言,浮点异常实际上从未在float除以零时出现,而只在integer被零时出现。

这听起来很奇怪,因为我知道:

  1. MSVC在所有Windows平台上的x86和x64上编译的代码报告整数除以零为"0xc0000094:整数除以零",浮点除以零为0xC000008E"浮点除以零"(启用时)

  2. IA-32和AMD64 ISAs将#DE(整数除法异常)指定为中断0。浮点异常触发中断16(x87浮点)或中断19(SIMD浮点)。

  3. 其他硬件也有类似的不同中断(例如PPC将floatdiv上的0x7000提高零,并且根本不捕获int/0)。

  4. 我们的应用程序使用_controlfp_s内在函数(最终为stmxcsr操作)来取消浮点异常的掩码,以便除以零,然后捕获它们以进行调试。所以我在实践中肯定看到了IEEE754除以零的例外。

因此,我得出的结论是,有些平台将int异常报告为浮点异常,例如x64 Linux(无论ALU管道如何,都会为所有算术错误引发SIGFPE)。

其他哪些操作系统(或者C/C++运行时,如果操作系统)将整数div乘以零报告为浮点异常?

我不确定目前的情况如何,但目前的情况是FP异常检测支持与integer非常不同。整数除法陷阱是很常见的POSIX要求它在引发异常的情况下引发SIGFPE

然而,您可以找出它是什么类型的SIGFPE,看看它实际上是一个除法异常。(但不一定要除以零:2的补码INT_MIN/-1除法陷波,x86的dividiv也会在64b/32b除法的商不适合32b输出寄存器时陷波。但使用sdiv的AArch64并非如此。)

glibc手册解释说,BSD和GNU系统为SIGFPE的信号处理程序提供了一个额外的arg,对于除以零,它将是FPE_INTDIV_TRAP。POSIX将FPE_INTDIV_TRAP记录为siginfo_tint si_code字段的可能值,在siginfo_t包含该成员的系统上。

IDK,如果Windows一开始就提供了不同的异常,或者它像Unix一样将事情捆绑到同一算术异常的不同风格中。如果是这样的话,默认处理程序会对额外的信息进行解码,告诉您这是哪种异常。

POSIX和Windows都使用短语"除以零"来涵盖所有整数除法异常,所以显然这是常见的简写。对于那些知道INT_MIN/-1(2的补码)是一个问题的人来说,短语"除以零"可以被视为除法例外的同义词。这个短语立即指出了人们不知道整数除法为什么会成为一个问题的常见情况。


FP异常语义

对于大多数操作系统/C ABI中的用户空间进程,默认情况下会屏蔽FP异常。

这是有道理的,因为IEEE浮点可以表示无穷大,并且具有NaN,可以使用该值将误差传播到未来的所有计算中。

  • 0.0/0.0=>NaN
  • 如果x是有限的:x/0.0=>符号为x的+/-Inf

这甚至允许像这样的事情在异常被屏蔽时产生合理的结果:

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)。在手动清除之前,该标志一直处于设置状态,因此您可以检查一次(例如在循环之后),以查看发生了哪些异常,但不能查看它们发生在哪里。

有人建议使用类似的"粘性"整数溢出标志来记录在一系列计算过程中的任何时候是否发生溢出。在某些情况下,允许屏蔽整数除法异常是很好的,但在其他情况下是危险的(例如,在地址计算中,您应该陷阱,而不是潜在地存储到伪造的位置)。

不过,在x86上,检测一系列计算过程中是否发生整数溢出需要在每一个计算之后放置一个条件分支,因为标志只是被覆盖。MIPS有一条add指令将捕获有符号溢出,还有一条无符号指令从不捕获。因此,整数异常检测和处理的标准化程度要低得多。


整数除法没有生成NaN或Inf结果的选项,因此它以这种方式工作是有意义的。

整数除法产生的任何整数位模式都是错误的,因为它将代表一个特定的有限值。

但是,在x86上,如果"浮点无效"异常被屏蔽,则使用cvtsd2si或任何类似的转换指令将超出范围的浮点值转换为整数会产生"不确定整数"值。该值除符号位外均为零。即CCD_ 23。

(请参阅英特尔手册,x86标记wiki.

中的链接

其他哪些操作系统(如果您是操作系统,则是C/C++运行时)将整数div乘以零报告为浮点异常?

答案取决于您是在内核空间还是用户空间。如果您在内核空间中,您可以将"i/0"放在kernel_main()中,让中断处理程序调用异常处理程序并停止内核。如果你在用户空间,答案取决于你的操作系统和编译器设置。

AMD64硬件将整数除以零指定为中断0,不同于中断16(x87浮点异常)和中断19(SIMD浮点异常)。

"被零除"异常用于使用div指令进行零除。讨论x87 FPU不在本问题的范围之内。

其他硬件也有类似的不同中断(例如PPC将floatdiv上的0x7000提高零,并且根本不捕获int/0)。

更具体地说,00700被映射到异常类型"Program",包括一个启用浮点的异常。如果尝试使用浮点指令除以零,则会引发这样的异常。

另一方面,整数除法是PPC-PEM的未定义行为:

8-53divw

如果尝试执行任一除法——0x8000_0000÷–1或÷0,则rD的内容未定义,如下所示CR0字段的LT、GT和EQ位的内容(如果Rc=1)。在这种情况下,如果OE=1,则设置OV。

我们的应用程序使用_controlfp_s内在函数(最终为stmxcsr-op)为除以零取消浮点异常的掩码,然后捕获它们以进行调试。所以我在实践中肯定看到了IEEE754除以零的例外。

我认为您的时间最好花在编译时上,而不是在运行时。

对于用户空间,这种情况发生在运行在POWER上的AIX、运行在PA-RISC上的HP-UX、运行在x86-64上的Linux、运行在x86-64上的macOS、运行在Alpha上的Tru64和运行在SPARC上的Solaris上。

避免在编译时被零除要好得多。