为什么要发生整数溢出?

时间:2011-02-02 21:17:58

标签: c# .net overflow integer-overflow

在这个问题中,主题是如何让VS检查C#中的算术溢出并抛出异常:C# Overflow not Working? How to enable Overflow Checking?

其中一条评论声称有些奇怪并且投票很多,我希望你能在这里帮助我:

  

您还可以使用checked关键字来包装语句或一组语句,以便显式检查它们是否存在算术溢出。设置项目范围的属性有点冒险,因为溢出通常是一个相当合理的期望。

我对硬件知之甚少,但我知道溢出与寄存器的工作方式有关。我一直认为溢出导致未定义的行为,应尽可能防止溢出。 (在“正常”项目中,不编写恶意代码)

为什么你会期望发生溢出?如果有可能,为什么不总是阻止它? (通过设置相应的编译器选项)

13 个答案:

答案 0 :(得分:35)

想要溢出的主要时间是计算哈希码。在那里,结果的实际数值大小根本不重要 - 它实际上只是我用碰巧算术操作的一个模式。

我们检查了Noda Time项目范围内的算法运行 - 我宁愿抛出异常而不是返回错误的数据。我怀疑溢出是非常罕见的......我承认我通常将默认值保留为未经检查的算术,因为它是默认值。当然还有速度惩罚......

答案 1 :(得分:10)

  

我一直以为溢出的原因   未定义的行为应该是   尽可能防止。

您可能也对缓冲区溢出(溢出)和数字溢出之间的区别感到困惑。

缓冲区溢出是指数据写入非托管数组的末尾。它可能导致未定义的行为,例如使用用户输入的数据覆盖堆栈上的返回地址。在托管代码中很难做到缓冲区溢出。

然而,数字溢出定义明确。例如,如果您有一个8位寄存器,它只能存储2 ^ 8个值(如果是无符号,则为0到255)。因此,如果你添加100 + 200,你就不会得到300,而是300模数256,这是44。使用签名类型的故事有点复杂;位模式以类似的方式递增,但它们被解释为two's complement,因此添加两个正数可以给出负数。

答案 2 :(得分:9)

使用不断递增的计数器进行计算时。一个典型的例子是Environment.TickCount:

int start = Environment.TickCount;
DoSomething();
int end = Environment.TickCount;
int executionTime = end - start;

如果检查过,那么程序在Windows启动27天后就有可能爆炸。当DoSomething运行时,TickCount超过int.MaxValue。 PerformanceCounter是另一个例子。

即使存在溢出,这些类型的计算也会产生准确的结果。第二个例子是你为生成代表性位模式所做的数学运算,你并不真正对准确的结果感兴趣,只是一个可重复的结果。这些例子是校验和,哈希和随机数。

答案 3 :(得分:5)

<强>角

溢出的整数是测量角度的优雅工具。你有0 == 0度,0xFFFFFFFF == 359.999 ....度。它非常方便,因为作为32位整数,你可以加/减角度(350度加20度最终溢出环绕回到10度)。您还可以决定将32位整数视为有符号(-180到180度)和无符号(0到360)。 0xFFFFFFF等于-179.999 ...,相当于359.999 ......,这是公平的。非常优雅。

答案 4 :(得分:3)

  

如果有可能,为什么不总是阻止它?

默认情况下未启用检查算术的原因是检查算术比未检查算术慢。如果性能对您来说不是问题,那么启用检查算术可能是有意义的,因为发生溢出通常是一个错误。

答案 5 :(得分:3)

生成HashCodes时,请从一串字符开始。

答案 6 :(得分:3)

这可能与历史有关,与任何技术原因一样。整数溢出通常被依赖于行为的算法(特别是哈希算法)用于良好的效果。

此外,大多数CPU设计为允许溢出,但在此过程中设置进位,这使得更容易在超过自然的字大小上实现添加。在此上下文中实现已检查的操作意味着如果设置了进位标志,则添加代码以引发异常。这不是一个巨大的强加,而是一个编译器编写者可能不想在没有选择的情况下强加给人们的。

替代方法是默认选中,但提供未选中的选项。为什么这不可能也可以追溯到历史。

答案 7 :(得分:2)

你可能会期望它是以增量衡量的东西。某些网络设备保持较小的计数器大小,您可以轮询一个值,比如传输的字节数。如果值太大,它只会溢出回零。如果你经常测量它(字节/分钟,字节/小时),它仍会给你一些有用的东西,并且由于计数器通常在连接断开时被清除,所以它们并不完全准确无关紧要。

正如贾斯汀所说,缓冲区溢出是一个不同的鱼。这是你在数组末尾写入内存的地方,你不应该这样做。在数字溢出中,使用相同数量的内存。在缓冲区溢出中,您使用未分配的内存。某些语言会自动阻止缓冲区溢出。

答案 8 :(得分:1)

有一个关于程序员在程序设计中利用溢出的经典故事:

The Story of Mel

答案 9 :(得分:1)

这与寄存器的工作原理无关,因为它只是存储数据的变量中的内存限制。 (您可以在内存中溢出变量而不会溢出任何寄存器。)

但要回答你的问题,请考虑最简单的校验和。它只是被检查的所有数据的总和。如果校验和溢出,那没关系,并且没有溢出的部分仍然有意义。

其他原因可能包括您只希望程序继续运行,即使无关紧要的变量可能已经溢出。

答案 10 :(得分:0)

我可以成像的另一种可能情况是随机数生成算法 - 在这种情况下我们不关心溢出,因为我们想要的只是一个随机数。

答案 11 :(得分:0)

整数溢出就像这样。

你有一个8位整数1111 1111,现在加1。 0000 0000,领先1被截断,因为它将处于第9位。

现在说你有一个有符号整数,前导位表示它是负数。所以现在你有0111 1111.加1,你有1000 0000,这是-128。在这种情况下,添加1到127会使其切换为负数。

我非常确定溢出的行为是以明确的方式进行的,但我不确定下溢。

答案 12 :(得分:0)

所有整数运算(至少添加减法和乘法)都是精确的。这只是对结果位的解释,您需要注意。在2的补码系统中,您可以得到2位模数的正确结果。有符号和无符号之间的唯一区别是对于有符号数,最高有效位被视为符号位。由程序员决定什么是合适的。显然,对于某些计算,您希望了解溢出并在检测到溢出时采取适当的操作。我个人从来不需要溢出检测。我使用依赖于它的线性同余随机数发生器,即64 * 64位无符号整数乘法,我只关心最低的64位,因为截断,我得到免费的模运算。