C#中处理整数溢出的最佳方法是什么?

53

处理整数溢出是一项常见任务,但在C#中处理它的最佳方法是什么?是否有一些语法糖可以使其比其他语言更简单?还是这确实是最好的方法?

int x = foo();
int test = x * common;
if(test / common != x)
    Console.WriteLine("oh noes!");
else
    Console.WriteLine("safe!");

5
最好的方法是在第一时间防止。 - Mitch Wheat
12
可以,但是这与此处提出的问题不同。处理它和预防它是两个不同的(当然相关的)讨论。 - dreadwail
6个回答

120

我很少需要使用这个功能,但您可以使用checked关键字:

int x = foo();
int test = checked(x * common);

如果溢出会导致运行时异常。来自MSDN:
在已检查的上下文中,如果表达式产生的值超出了目标类型的范围,则结果取决于表达式是常量还是非常量。常量表达式会导致编译时错误,而非常量表达式会在运行时进行评估并引发异常。
我还应该指出,还有另一个C#关键字,即unchecked,它当然与checked相反,并忽略溢出。你可能会想知道何时才会使用unchecked,因为它似乎是默认行为。好吧,有一个C#编译器选项,定义了如何处理checked和unchecked之外的表达式:/checked。您可以在项目的高级构建设置下设置它。
如果您有许多需要检查的表达式,则最简单的方法实际上是设置/checked构建选项。然后,除非包含在unchecked中,否则任何溢出的表达式都会导致运行时异常。

3
默认情况下为什么不勾选?使用勾选有性能问题吗? - KFL
3
@KFL “因为检查溢出需要时间,在没有溢出危险的情况下使用未经检查的代码可能会提高性能。但是,如果存在溢出的可能性,则应使用已检查的环境。”请参见http://msdn.microsoft.com/en-us/library/a569z7k8.aspx。我还相信默认情况下未经检查,因为它与C / C ++行为相似。 - Michael Petito
1
有些情况下,溢出是有用的,例如 TCP 序列号。 无符号整数会重新从零开始,所以只需要使用递增运算符即可。 - user472308
在所有机器上,未经检查的溢出是否会产生相同的结果? - user5528169
'checked' 的更新 URL 引用 - https://msdn.microsoft.com/zh-cn/library/74b4xzyw(v=vs.140).aspx - Saurabh Kumar
这对于意外的溢出非常有用。如果溢出经常发生,请注意异常增加的开销。 - hultqvist

22

尝试以下操作

int x = foo();
try {
  int test = checked (x * common);
  Console.WriteLine("safe!");
} catch (OverflowException) {
  Console.WriteLine("oh noes!");
}

8

最好的方法是像Michael所说的那样 - 使用Checked关键字。 可以这样做:

int x = int.MaxValue;
try   
{
    checked
    {
        int test = x * 2;
        Console.WriteLine("No Overflow!");
    }
}
catch (OverflowException ex)
{
   Console.WriteLine("Overflow Exception caught as: " + ex.ToString());
}

你需要格式化你的代码(在代码前插入4个空格,网站会自动识别格式和语法高亮显示)。 - dreadwail

6
有时候,最简单的方法就是最好的方法。我认为你写的已经很好了,但你可以把它缩短成:
int x = foo();

if ((x * common) / common != x)
    Console.WriteLine("oh noes!");
else
    Console.WriteLine("safe!");

请注意,我没有删除变量x,因为调用foo()三次是愚蠢的。

1
现在我想知道编译器是否会完全优化掉那个表达式? - Michael Petito
3
除非您打算使用结果(x*common),否则这是有用的;如果要使用结果,缩短将需要计算两次... - Cobusve

4

虽然这个帖子比较老,但我最近遇到了同样的问题。我不想使用异常机制。最终我采用了以下方法:

long a = (long)b * (long)c;
if(a>int.MaxValue || a<int.MinValue)
    do whatever you want with the overflow
return((int)a);

1
这样是行不通的。int.MaxValue + 1会给你-2147483648或int.MinValue。所以a永远不能比MaxValue更大或比MinValue更小。把数字想象成一条线,就像这样:-2147483648,-2147483647,... 0 ... 2147483646,2147483647,当你超过最大值时,它会回流到最小值。 - Ray Cheng
7
你忽略了他/她在检查 int 的边界之前转换成了 long。因此,只要不溢出 long ,它就能够工作。 - hammett

0

所以,我在事后才遇到了这个问题,它基本上回答了我的问题,但对于我的特定情况(如果有其他人有相同的要求),我希望任何超出有符号整数正值的内容都能够被设定为int.MaxValue

int x = int.MaxValue - 3;
int someval = foo();

try
{
   x += someval;
}

catch (OverflowException)
{
   x = int.MaxValue;
}

2
除非您使用/checked编译选项或使用checked关键字,否则异常不会被抛出,对吗? - Paul Knopf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接