【发布时间】:2010-10-08 16:14:38
【问题描述】:
我有几个关于 x86 或 x86_64 架构上的除法溢出错误的问题。最近我一直在阅读有关整数溢出的文章。通常,当算术运算导致整数溢出时,FLAGS 寄存器中的进位位或溢出位被置位。但显然,根据this article,除法运算导致的溢出不会设置溢出位,而是触发硬件异常,类似于除以零时。
现在,除法导致的整数溢出比乘法少得多。只有几种方法可以触发除法溢出。一种方法是执行以下操作:
int16_t a = -32768;
int16_t b = -1;
int16_t c = a / b;
在这种情况下,由于有符号整数的二进制补码表示,不能用带符号的16位整数表示正32768,所以除法运算溢出,导致-32768的错误值。
几个问题:
1) 与本文所说的相反,以上并没有导致硬件异常。我正在使用运行 Linux 的 x86_64 机器,当我除以零时,程序以 Floating point exception 终止。但是当我导致除法溢出时,程序照常继续,默默地忽略错误的商。那么为什么这不会导致硬件异常呢?
2) 为什么硬件对除法错误的处理如此严重,而不是其他算术溢出?为什么硬件会默默地忽略乘法溢出( 更容易发生意外),而除法溢出却应该触发致命中断?
=========== 编辑 ==============
好的,谢谢大家的回复。我收到的回复基本上说上述 16 位整数除法不应该导致硬件故障,因为商仍然小于寄存器大小。我不明白这一点。在这种情况下,存储商的寄存器是 16 位的 - 太小无法存储带符号的正数 32768。那么为什么不引发硬件异常?
好的,让我们直接在 GCC 内联汇编中执行此操作,看看会发生什么:
int16_t a = -32768;
int16_t b = -1;
__asm__
(
"xorw %%dx, %%dx;" // Clear the DX register (upper-bits of dividend)
"movw %1, %%ax;" // Load lower bits of dividend into AX
"movw %2, %%bx;" // Load the divisor into BX
"idivw %%bx;" // Divide a / b (quotient is stored in AX)
"movw %%ax, %0;" // Copy the quotient into 'b'
: "=rm"(b) // Output list
:"ir"(a), "rm"(b) // Input list
:"%ax", "%dx", "%bx" // Clobbered registers
);
printf("%d\n", b);
这只会输出一个错误的值:-32768。仍然没有硬件异常,即使存储商 (AX) 的寄存器太小而无法容纳商。所以我不明白为什么这里没有引发硬件故障。
【问题讨论】:
-
您在谈论特定于硬件的机器语言级别的行为,但您正在使用 C 代码对其进行测试。那是没有意义的。您为什么要尝试将汇编指令规范应用于 C 语言除法运算符?有些东西让您认为您的 C 部门实际上生成了您引用的规范的除法指令?您确认是这种情况吗?
-
@AndreyT,好的,我更新了问题,直接用汇编语言尝试了
-
您在
DX:AX对中错误地表示了-32768。实际上,您将+32768除以-1,这就是为什么得到-32768。看我的回答。 -
在 32 位
int的实现上,您的示例不会导致除法溢出。它产生一个完美可表示的int, 32768,然后在您进行分配时以实现定义的方式转换为int16_t。 (提示:查找默认促销)
标签: c++ c assembly x86 integer-division