c++ - 在哪些平台上整数除以零会触发浮点异常?

标签 c++ c error-handling x86-64 divide-by-zero

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

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

  • 所有 Windows 平台上 x86 和 x64 上的 MSVC 编译代码报告 int 除以零为“0xc0000094:整数除以零”,浮点除以零为 0xC000008E“浮点除以零”(启用时)
  • IA-32 and AMD64 ISA 指定 #DE (integer divide exception)作为中断 0。浮点异常触发中断 16(x87 浮点)或中断 19(SIMD 浮点)。
  • 其他硬件具有类似的不同中断(例如,PPC 在 float-div-by-zero 上引发 0x7000 并且根本不会捕获 int/0)。
  • 我们的应用程序使用 _controlfp_s 来消除被零除的浮点异常。内在(最终 stmxcsr op)然后捕获它们以进行调试。所以我在实践中肯定见过 IEEE754 除零异常。

  • 所以我得出结论,有一些平台将int异常报告为浮点异常,例如x64 Linux (raising SIGFPE for all arithmetic errors regardless of ALU pipe) .

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

    最佳答案

    我不确定当前的情况是如何发生的,但目前的情况是 FP 异常检测支持与整数非常不同。整数除法陷入困境是很常见的。 POSIX requires it to raise SIGFPE 如果它引发异常。

    但是,你可以梳理一下它是什么类型的SIGFPE,看看它实际上是一个除法异常。 (虽然不一定被零除:2 的补码 INT_MIN/-1 除法陷阱,当 64b/32b 除法的商不适合 32b 输出寄存器时,x86's div and idiv 也会陷阱。但事实并非如此AArch64 using sdiv 上的案例。)

    glibc manual explains BSD 和 GNU 系统为 SIGFPE 的信号处理程序提供了一个额外的参数。 ,这将是 FPE_INTDIV_TRAP除以零。 POSIX 文档 FPE_INTDIV_TRAP作为 siginfo_t 的可能值的 int si_code领域,在 siginfo_t 的系统上包括那个成员。

    IDK 如果 Windows 首先提供不同的异常,或者它是否像 Unix 那样将事物捆绑到相同算术异常的不同风格中。如果是这样,默认处理程序会解码额外信息以告诉您它是哪种异常。

    POSIX 和 Windows 都使用短语“被零除”来涵盖所有整数除法异常,因此显然这是常见的速记。对于确实知道 INT_MIN/-1(带有 2 的补码)是一个问题的人,短语“被零除”可以视为除法异常的同义词。对于不知道为什么整数除法可能是一个问题的人来说,这句话立即指出了常见的情况。

    FP 异常语义

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

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

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

  • 当异常被屏蔽时,这甚至允许这样的事情产生合理的结果:
    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)。该标志保持设置直到手动清除,因此您可以检查一次(例如在循环之后)以查看发生了哪些异常,而不是它们发生的位置。

    已有proposals for having similar "sticky" integer-overflow flags记录在一系列计算过程中的任何一点是否发生溢出。允许屏蔽整数除法异常在某些情况下会很好,但在其他情况下很危险(例如,在地址计算中,您应该捕获而不是潜在地存储到虚假位置)。

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

    整数除法没有产生 NaN 或 Inf 结果的选项,所以它以这种方式工作是有意义的 .

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

    但是,在 x86 上,使用 cvtsd2si or any similar conversion instruction 将超出范围的浮点值转换为整数如果“浮点无效”异常被屏蔽,则产生“整数不定”值。除符号位外,该值全为零。即 INT_MIN .

    (请参阅英特尔手册、 标签维基中的链接。

    关于c++ - 在哪些平台上整数除以零会触发浮点异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37262572/

    相关文章:

    c++在没有重载的情况下接收const左值和右值引用参数

    c++ - 试图了解 C 预处理器

    c++ - boost::lexical_cast 可以将字符串中的十六进制转换为整数吗?

    c - malloc(sizeof(struct Node)) 与 malloc(sizeof(nodeptr)) 的不同行为

    c++ - 为什么 snprintf 比 ostringstream 快,还是这样?

    c++ - 如何避免调试宏中的 move 构造函数?

    c - 如何使用 fprintf 并写入管道?

    java - 自定义内部服务器错误消息 Play Framework

    python - 处理 python 中的错误

    node.js - 如何捕获 node.js 中的 utf-8 解码错误?