我有一些关于x86或x86_64体系结构上的划分溢出错误的问题。最近,我一直在阅读有关整数溢出的知识。通常,当算术运算导致整数溢出时,将置位FLAGS寄存器中的进位或溢出位。但是很显然,根据本文所述,除法运算导致的溢出不会设置溢出位,而是会触发硬件异常,类似于将其除以零时的情况。

现在,除法导致的整数溢出比乘法要少得多。只有几种方法可以触发除法溢出。一种方法是做类似的事情:

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

", b);

这只是输出一个错误值:-32768。即使存储商数(AX)的寄存器太小而无法容纳商数,仍然没有硬件异常。所以我不明白为什么这里没有出现硬件故障。

您在谈论硬件特定的机器语言级别的行为,但是您正在使用C代码对其进行测试。 那只是没有道理。 您为什么要尝试将汇编指令规范应用于C语言除法运算符? 是什么让您认为您的C除法实际上生成了您引用其规范的除法指令? 您是否验证了这种情况?

@AndreyT,好点-我更新了问题,以直接在汇编语言中尝试

您在DX:AX对中错误地表示了-32768。 实际上,您将+32768除以-1,这就是为什么要获取-32768的原因。 看我的答案。

在使用32位int的实现中,您的示例不会导致除法溢出。 它产生一个完美可表示的int 32768,然后在进行分配时以实现定义的方式将其转换为int16_t。 (提示:查找默认促销)

在C语言中,永远不会在小于int的类型内执行算术运算。每当您尝试对较小的操作数进行算术运算时,它们都会首先进行积分提升,然后将其转换为int。如果您的平台上的int是32位宽,则无法强制C程序执行16位除法。编译器将改为生成32位除法。这可能是为什么您的C实验未在除法上产生预期的溢出的原因。如果您的平台确实有32位int,那么最好的选择是尝试使用32位操作数进行相同的操作(即将INT_MIN除以-1)。我非常确定,即使在C代码中,您最终也将能够重现溢出异常。

在汇编代码中,由于将BX指定为idiv的操作数,因此您使用16位除法。 x86上的16位除法将DX:AX对中存储的32位除数除以idiv操作数。这就是您在代码中所做的事情。 DX:AX对被解释为一个32位复合寄存器,这意味着该对中的符号位实际上是DX的最高位。 AX的最高位不再是符号位。

您对DX做了什么?您只需清除它。您将其设置为0。但是DX设置为0时,您的股息被解释为正数!从机器角度来看,这样的DX:AX对实际上表示正值+32768。即在您的汇编语言实验中,您将+32768除以-1。结果应该是-32768。这里没什么异常。

如果要在DX:AX对中表示-32768,则必须对其进行符号扩展,即必须用全1的位模式而不是0填充DX。您应该先用-32768初始化AX,然后再完成cwd,而不是执行xor DX, DX。那会将AX的符号扩展为DX。

例如,在我的实验(不是GCC)中,此代码

__asm  {

mov AX, -32768

cwd

mov BX, -1

idiv BX

}

导致预期的异常,因为它确实试图将-32768除以-1。

好吧,我明白了。谢谢。这触发了预期的硬件故障。

汇编语言示例不错!

当您得到带有整数2的补码加/减/乘的整数溢出时,您仍然可以获得有效的结果-它只是缺少一些高阶位。此行为通常很有用,因此不适合为此生成异常。

但是,对于整数除法,除以零的结果是无用的(因为与浮点数不同,2的补码整数没有INF表示)。

在有关整数溢出的相关部分中:

Unlike the add, mul, and imul

instructions, the Intel division

instructions div and idiv do not set

the overflow flag; they generate a

division error if the source operand

(divisor) is zero or if the quotient

is too large for the designated

register.

在现代平台上,寄存器的大小为32位或64位; 32768将适合这些寄存器之一。但是,以下代码很可能会引发整数溢出执行(它在VC8的我的双核笔记本电脑上执行):

int x= INT_MIN;

int y= -1;

int z= x/y;

Contrary to what this article says, the above did NOT cause a hardware exception

文章没有这么说。是说

... they generate a division error if the source operand (divisor) is zero or if the quotient is too large for the designated register

寄存器大小肯定大于16位(32 || 64)

但是在这种情况下,存储商的寄存器只有16位,那么为什么不引发硬件异常呢?请参阅我编辑过的问题以进一步说明。

@ Channel72,存储商的类型只有16位。寄存器本身是32位。

@MSN:在x86中,寄存器大小是可变的。 ax甚至在64位计算机上也是16位寄存器,这是解释结果在此处如何截断的唯一方法。

@MSN,我很困惑。 AX寄存器存储商,并且AX寄存器为16位

@Potatoswatter,嗯,嗯,编辑过的帖子中的程序集是错误的(请参阅andreyTs评论),但是我衷心地怀疑gcc是否会使用16位操作码进行int16_t除法。该标准并没有规定除法运算的实际执行方式,而仅是其结果。无论如何,您仍然可以得到整数除数,但是您不应该尝试使用小于int的类型(因为无论如何您只能使用INT_MIN / -1进行处理)。

我尝试了C代码,它实际上在Visual Studio中生成了idiv eax,ecx。

我猜想在某些旧计算机上,尝试被零除会导致一些严重的问题(例如,将硬件置于一个无穷循环中,试图进行足够的减法,以便在操作员进行修复之前,剩余的余数小于红利。 ),这开始了除数溢出的传统,被认为比整数溢出更严重的故障。

从编程的角度来看,没有理由认为意外的除法溢出应该比意外的整数溢出(有符号或无符号)更严重。考虑到划分的成本,事后检查溢出标志的边际成本将很小。传统是我看到具有硬件陷阱的唯一原因。

在使用32位int的实现中,您的示例不会导致除法溢出。它产生一个完美可表示的int 32768,然后在进行分配时以实现定义的方式将其转换为int16_t。这是由于C语言指定的默认提升,因此,在此处引发异常的实现将不符合要求。

如果您想尝试引发异常(实际上仍然可能会发生或可能不会发生,这取决于实现),请尝试:

int a = INT_MIN, b = -1, c = a/b;

您可能需要做一些技巧,以防止编译器在编译时对其进行优化。

您的示例未生成硬件异常的原因是由于C的整数提升规则。小于int的操作数将在执行操作之前自动提升为ints。

至于为什么对不同类型的溢出进行不同处理的原因,请考虑在x86机器级别上,确实没有乘法溢出的事情。当您将AX与其他寄存器相乘时,结果进入DX:AX对,因此结果始终有空间,因此没有机会发出溢出异常信号。但是,在C语言和其他语言中,两个ints的乘积应该适合int,因此在C级别会发生溢出。 x86有时会在MUL上设置OF(溢出标志),但这仅意味着结果的大部分不是零。

80386的乘以常数指令使用与源大小相同的单个目标寄存器。它们在溢出的情况下设置标志,该标志可能会也可能不会被忽略。

c语言除法结果溢出怎么办,关于C ++:导致除法溢出错误(x86)相关推荐

  1. arthas调查内存溢出 kibana宕机导致内存溢出

    1.prometheus报告,服务内存不足预警: 只运行了4天,JVM 启动参数是2G,平时 足够使用 2.使用jstat命令查看gc状态 显示频繁ygc.full gc,问题严重 3.使用artha ...

  2. linux内核启用64位除法,关于内核中的乘法和除法。

    关于内核中的乘法和除法. 作者:heziq 发布于:2015-5-6 22:02 前几天一直在看wowo的时间子系统,一直在思索mult和shift变量,为什么mult要尽量大,shift尽量小.这是 ...

  3. 导致堆栈溢出的原因什么?

    导致堆栈溢出的原因: 1. 函数调用层次太深.函数递归调用时,系统要在栈中不断保存函数调用时的现场和产生的变量,如果递归调用太深,就会造成栈溢出,这时递归无法返回.再有,当函数调用层次过深时也可能导致 ...

  4. python多线程内存溢出_Python内存泄漏和内存溢出的解决方案

    一.内存泄漏 像Java程序一样,虽然Python本身也有垃圾回收的功能,但是同样也会产生内存泄漏的问题. 对于一个用 python 实现的,长期运行的后台服务进程来说,如果内存持续增长,那么很可能是 ...

  5. c语言中较常见的由内存分配引起的错误_内存越界_内存未初始化_内存太小_结构体隐含指针...

    1.指针没有指向一块合法的内存 定义了指针变量,但是没有为指针分配内存,即指针没有指向一块合法的内浅显的例子就不举了,这里举几个比较隐蔽的例子. 1.1结构体成员指针未初始化 1 2 3 4 5 6 ...

  6. 单行文本溢出显示省略号,单行文本溢出显示省略号

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. 添加IFrame导致内存溢出的解决过程(IE浏览器,目前发现了原因,还未解决)...

    1.  现象 每次动态添加iframe时,iexplore.exe进程占据的内存都会增加(大概10M左右),不会自动释放,最终导致内存溢出 2.  解决过程 经过网络的一番搜索,基本上给出的解决方案是 ...

  8. java内存溢出怎样查找代码_JVM - 内存溢出,问题查找

    当内存溢出会抛出 java.lang.OutOfMemoryError: Java heap space 的异常,那这个时候怎样去分析到底哪里导致内存溢出呢? 我们可以通过在vm的参数, -XX:+H ...

  9. 本地项目antd 修改.less文件导致内存溢出

    项目场景: antd 项目,修改less文件会导致内存溢出 问题描述 本地环境antd 项目,修改less文件会导致内存溢出:如下 FATAL ERROR: Reached heap limit Al ...

最新文章

  1. 关于chrome等浏览器不支持showModalDialog的解决方案
  2. 通过IEnumerable和IDisposable实现可暂停和取消的任务队列
  3. 如何寻回xp盘符丢失的数据
  4. gym 102875A -- Array(未更新完)
  5. 快照是什么?揭秘存储快照的实现
  6. java调用app接口代码_java servlet手机app访问接口(二)短信验证
  7. WinCE --- 调试RS485串口
  8. 生产宕机dunp配置
  9. linux下find命令-atime,-ctime,-mtime真正含义
  10. php中读取session,php中如何注册和读取Session会话
  11. 2021 年“泰迪杯”数据分析技能赛 B 题 肥料登记数据分析
  12. Windows常用快捷键和Windows CMD命令大全
  13. 陈玉琴答问: 经络按摩与敲胆经(转载)
  14. python字符串转负数_python 字符串 步进 负数
  15. 【圆梦名企第三季】4月12日软件业“人才留湘 引才入湘”专项行动
  16. 小说:凡人修仙路基础
  17. 苹果7支持快充吗_苹果支持20W快充了,什么20W还叫快充,国内都到120W了
  18. Fuzzy SVM with a new fuzzy membership function--文献翻译
  19. 高级查询组件dynamicCondition升级为v2.0.0版本(一)——使用步骤
  20. 安全帽识别软件能够解决现场管理中的很多问题

热门文章

  1. java 裁剪网格纸_百度裁减网格纸
  2. 多图理解MySQL事务的隔离等级,脏读,不可重复读,幻读的几大概念
  3. 如何向瑞芯微平台添加驱动
  4. Python爬取搜集豆瓣图书集,书荒的朋友们再也不用担心了
  5. 后台管理系统-------登录功能@zj-zhangjie
  6. LINUX系统镜像下载总汇
  7. 计算机复试中的一些问题
  8. OpenSceneGraph-3.6.4-VC2015-x86 编译发布
  9. 听哈佛商学院教授讲:如何建立信任
  10. 碰到 stdafx.h报错