Cra*_*rks 11 c c++ error-handling x86-64 divide-by-zero
在另一个问题中,有人想知道为什么他们得到一个"浮点错误",实际上他们的C++程序中有一个整数除零.围绕这一点进行了讨论,其中一些断言浮点异常实际上从未因浮点除以零而增加,而只是在整数除以零时出现.
这听起来很奇怪,因为我知道:
所有Windows平台上x86和x64上的MSVC编译代码报告int除以零为"0xc0000094:整数除以零",浮点除以零为0xC000008E"浮点除以零"(启用时)
IA-32和AMD64 ISA指定#DE
(整数除法异常)作为中断0.浮点异常触发中断16(x87浮点)或中断19(SIMD浮点).
其他硬件具有类似的不同中断(例如, PPC在float-div-by上引发0x7000并且根本不捕获int/0).
我们的应用程序使用_controlfp_s
内部(最终stmxcsr
操作)取消屏蔽零除零的浮点异常,然后捕获它们以进行调试.所以我在实践中肯定会看到IEEE754被零除的异常.
因此,我得出结论,有些平台将异常作为浮点异常报告,例如x64 Linux(无论ALU管道如何,都会针对所有算术错误提升SIGFPE).
其他操作系统(如果您是操作系统,还是C/C++运行时)报告整数除零作为浮点异常?
Pet*_*des 11
我不确定目前的情况如何,但目前的情况是FP异常检测支持与整数非常不同.陷阱的整数除法很常见. POSIX要求它提升,SIGFPE
如果它根本引发异常.
但是,你可以理清它是什么类型的SIGFPE,看它实际上是一个除法异常.(但不一定是被0除以:2的补码INT_MIN
/ -1
除法陷阱,以及当64b/32b除法的商不适合32b输出寄存器时x86 div
和idiv
陷阱.)
所述的glibc手册说明那BSD和GNU系统用于信号处理程序提供一个额外的精氨酸sdiv
,这将是SIGFPE
对被零除.POSIX文档FPE_INTDIV_TRAP
作为包含该成员的系统FPE_INTDIV_TRAP
的siginfo_t
字段的可能值int si_code
.
IDK,如果Windows首先提供不同的异常,或者它将事物捆绑到与Unix相同的算术异常的不同风格中.如果是这样,默认处理程序会对额外信息进行解码,以告诉您它是什么类型的异常.
POSIX和Windows都使用短语"除以零"来涵盖所有整数除法异常,所以显然这是常见的简写.对于知道关于INT_MIN/-1(带有2的补码)的人来说是一个问题,短语"除以零"可以被视为除法异常的同义词.这句话立即指出了那些不知道为什么整数除法可能成为问题的人的常见情况.
对于大多数操作系统/ C ABI中的用户空间进程,FP异常在默认情况下被屏蔽.
这是有道理的,因为IEEE浮点可以表示无穷大,并且有NaN将错误传播到使用该值的所有未来计算.
siginfo_t
=> 0.0/0.0
NaN
是有限的: x
=> x/0.0
带有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
Run Code Online (Sandbox Code Playgroud)
FP检测错误的方法非常好:当屏蔽异常时,它们在FP状态寄存器中设置一个标志而不是陷阱.(例如x86的MXCSR用于SSE指令).标志保持设置直到手动清除,因此您可以检查一次(例如循环之后)以查看发生了哪些异常,但不是它们发生的位置.
已经提出了具有类似"粘性"整数溢出标志的建议,以记录在计算序列期间的任何点处是否发生溢出.允许屏蔽整数除法异常在某些情况下会很好,但在其他情况下会很危险(例如,在地址计算中,您应该捕获而不是潜在地存储到虚假位置).
但是,在x86上,检测在计算序列期间是否发生整数溢出需要在每一个之后放置一个条件分支,因为标志只是被覆盖.MIPS有一条+/-Inf
指令可以捕获有符号的溢出,以及一条永不陷阱的无符号指令.因此,整数异常检测和处理的标准化程度要低得多.
整数除法不能选择产生NaN或Inf结果,因此以这种方式工作是有意义的.
整数除法产生的任何整数位模式都是错误的,因为它将代表一个特定的有限值.
但是,在x86上,如果屏蔽了"浮点无效"异常,则将超出范围的浮点值add
转换为带有或任何类似转换指令的整数会产生"整数不定"值.除符号位外,该值为全零.即cvtsd2si
.
(参见英特尔手册,x86标签wiki 中的链接.
小智 2
\n\n\n还有哪些其他操作系统(或 C/C++ 运行时,如果您是操作系统)将整数除以零报告为浮点异常?
\n
答案取决于您是在内核空间还是用户空间。如果您在内核空间中,则可以将“i / 0”放入kernel_main()
,让中断处理程序调用异常处理程序并停止内核。如果您位于用户空间,答案取决于您的操作系统和编译器设置。
\n\n\nAMD64 硬件将整数除以零指定为中断 0,与中断 16(x87 浮点异常)和中断 19(SIMD 浮点异常)不同。
\n
“除以零”例外是用指令除以零div
。讨论 x87 FPU 超出了本问题的范围。
\n\n\n其他硬件也有类似的不同中断(例如,PPC 在浮点除以零时引发 0x7000,并且根本不捕获 int/0)。
\n
更具体地说,00700
映射到异常类型“Program”,其中包括启用浮点的异常。如果尝试使用浮点指令除以零,则会引发此类异常。
另一方面,根据 PPC PEM,整数除法是未定义的行为:
\n\n\n\n\n\n\n8-53分区
\n\n如果尝试执行除法 \xe2\x80\x940x8000_0000 \xc3\xb7\n \xe2\x80\x931 或 \xc3\xb7 0 中的任何一个,则 rD 的内容未定义,\n 的内容也是如此CR0 字段的 LT、GT 和 EQ 位的值(如果 Rc = 1)。\n 在这种情况下,如果 OE = 1,则设置 OV。
\n
\n\n\n我们的应用程序使用内在函数(最终是 stmxcsr 操作)揭露被零除的浮点异常
\n_controlfp_s
,然后捕获它们以进行调试。所以我在实践中确实见过 IEEE754 除零异常。
我认为你的时间最好花在编译时捕获除零而不是运行时。
\n