C#中的整型数据类型没有溢出异常?

81

我在Project Euler中遇到了一个奇怪的问题,与10号问题有关。任务是计算小于两百万的所有质数之和。

我使用了int类型的变量作为求和结果,我的算法得出了答案,但当我将它粘贴验证答案时,发现它是错误的。

后来发现,结果太大无法容纳在int类型中,但这难道不应该导致溢出错误之类的吗?相反,它只返回了一个远离实际答案的值。

当我将类型更改为long后,一切都很好。


5
你真的希望每个整数操作都检查溢出吗? - John Saunders
6
好吧,在这种特殊情况下,这确实可以节省我一些时间 ;) - erikric
1
在这种情况下,是的。绝大多数操作不可能溢出。如果编译器能够证明这一点并因此禁用检查,那将是很有趣的,但我非常怀疑它是否会这样做。 - Thorarin
6个回答

112

默认情况下,C#整数运算不会在溢出时抛出异常。您可以通过项目设置或通过进行 checked 计算来实现此目的:

int result = checked(largeInt + otherLargeInt);

现在这个操作会抛出异常。

相反的是unchecked,它使得任何操作都被显式地视为未经检查的。很明显,这只有在项目设置中启用了检查操作时才有意义。


除非您处于调试模式,否则我认为 int 操作默认不会被检查。 - Ant
8
不,这是在调试模式下发生的。 - erikric
哦,我改正了。我确信我的项目欧拉项目会在调试模式下告诉我关于溢出的问题。:/ - Ant
8
显然,这仅在项目设置中启用了已检查的操作时才有意义。... 或者你想在“已检查”的上下文中评估一个“未检查”的表达式。你可以将这些块嵌套起来。 - Mehrdad Afshari
当结果值溢出时会发生什么 - 假设您正在为校验和添加一堆字节,并且校验和结果是一个字节。如果它溢出,您并不关心,只想继续添加字节。结果是否会在溢出时变为0并继续累加? - Neal Davis
1
checked块或表达式中,会抛出一个OverflowException。如果您不关心溢出,请不要检查它们。您的校验和示例将是一个典型的用例,您不想检查溢出。这通常会循环(而不是归零)。但是,请注意,C#(和其他语言)默认情况下会执行一些称为“整数提升”的聪明操作。添加两个字节的结果是一个整数。如果直接打印它,结果可能比byte可以容纳的要大。您需要重新分配它到一个byte(或进行强制转换)以截断它。 - Konrad Rudolph

27

在 C# 中,OverflowException 不会被抛出(在 VB 中默认会抛出该异常)。

若要抛出该异常,您需要将代码嵌入到 checked 上下文中:

byte value = 241;
checked
{
    try 
    {
        sbyte newValue = (sbyte) value;
        Console.WriteLine("Converted the {0} value {1} to the {2} value {3}.", 
            value.GetType().Name, value, 
            newValue.GetType().Name, newValue);
    }
    catch (OverflowException) 
    {
        Console.WriteLine("Exception: {0} > {1}.", value, SByte.MaxValue);
    }
}       

MSDN有更详细的解释:

对于算术、类型转换等操作如果要抛出OverflowException,必须发生在已启用检查的上下文中。默认情况下,Visual Basic中的算术运算和溢出是已启用检查的;而在C#中则没有。如果该操作发生在未启用检查的上下文中,则结果通过丢弃任何无法适应目标类型的高位比特来进行截断。


22

这是因为默认情况下,C#不会对整数溢出和下溢抛出任何异常。您可以在此处执行几个操作。

选项1

您需要启用异常才能被抛出,方法是进入项目 => 属性 => 生成选项卡 => 高级 => 检查算术溢出下溢(确保您选中该选项)。

enter image description here

请确保您选中该选项

选项2

使用“checked”块并抛出溢出异常以处理情况。一个代码示例片段如下:

        try
        {
            checked
            {
                int y = 1000000000;
                short x = (short)y;
            }
        }
        catch (OverflowException ex)
        {
            MessageBox.Show("Overflow");
        }
        catch (Exception ex)
        {
            MessageBox.Show("Error");
        }

9

我已经添加了一条评论,但是对于您中的一些人可能会感兴趣:

msdn 告诉我们:

整数算术溢出会抛出OverflowException或舍弃结果的最高有效位

但是

十进制算术溢出总是抛出OverflowException。

另外

当整数溢出发生时,会发生什么取决于执行上下文,可以检查或未检查。在检查上下文中,将抛出OverflowException。在未检查的上下文中,结果的最高有效位将被舍弃,执行将继续。因此,C#为您提供了处理或忽略溢出的选择。


1
这是有道理的,因为小数通常用于货币或科学计算,你可能更愿意让它们抛出异常而不是默默地失败。 - Snellface

6

默认情况下,C# 不会检查整数的算术溢出。您可以使用 /checked 编译器选项 或在 Visual Studio 中启用“检查算术溢出/下溢”(项目属性-生成-高级)来更改此设置。

您可以使用checkedunchecked 关键字 以按需覆盖默认设置。如果您依赖于代码中的检查操作,则明确使用 checked 启用检查是个好主意。

int j = checked(i * 2);

checked
{
    int j = i * 2;
    // Do more stuff
}

请注意,浮点运算永远不会抛出 OverflowException 异常,而十进制运算始终会抛出 OverflowException 异常。另请参见 C# 运算符

0

您可以通过添加以下内容,在csproj中直接设置checked

  <PropertyGroup>
    <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
  </PropertyGroup>

这相当于在设计师中勾选 检查算术溢出/下溢 复选框。


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