48
int y = -2147483648;
int z = unchecked(y / -1);

第二行导致OverflowException. 不应该unchecked阻止吗?

例如:

int y = -2147483648;
int z = unchecked(y * 2);

不会导致异常。

4

3 回答 3

35

这不是 C# 编译器或抖动可以控制的例外。它特定于 Intel/AMD 处理器,当 IDIV 指令失败时,CPU 会生成 #DE 陷阱(除法错误)。操作系统处理处理器陷阱并将其反射回进程,并带有 STATUS_INTEGER_OVERFLOW 异常。CLR 尽职尽责地将其转换为匹配的托管异常。

英特尔处理器手册并不完全是有关它的信息的金矿:

非整数结果被截断(截断)到 0。余数总是小于除数的大小。溢出用#DE(除法错误)异常而不是用 CF 标志指示。

英文:有符号除法的结果是 +2147483648,不能用int表示,因为它是 Int32.MaxValue + 1。否则处理器表示负值的方式不可避免的副作用,它使用二进制补码编码。它产生一个表示 0 的值,留下奇数个其他可能的编码来表示负值和正值。还有一个负值。与 相同类型的溢出-Int32.MinValue,只是处理器不会捕获 NEG 指令而只会产生垃圾结果。

C# 语言当然不是唯一存在这个问题的语言。C# 语言规范通过注意特殊行为使其实现定义的行为(第 7.8.2 章)。他们无法用它做任何其他合理的事情,生成处理异常的代码肯定被认为太不切实际,产生无法诊断的慢代码。不是 C# 方式。

C 和 C++ 语言通过使其行为未定义来规范赌注。这真的会变得很难看,就像使用 gcc 或 g++ 编译器编译的程序一样,通常使用 MinGW 工具链。它对 SEH 的运行时支持不完善,它吞下异常并允许处理器重新启动除法指令。程序挂起,烧毁 100% 内核,处理器不断生成#DE 陷阱。将师变成传说中的停止和着火指令:)

于 2014-10-27T19:02:55.203 回答
33

C# 4 规范的第 7.72 节(除法运算符)指出:

如果左操作数是可表示的最小 int 或 long 值,而右操作数是 –1,则会发生溢出。在检查的上下文中,[...]。在未经检查的上下文中,是否抛出 System.ArithmeticException(或其子类)或溢出未报告,结果值是左操作数的值,是实现定义的。

因此,这在未经检查的上下文中引发异常的事实实际上并不是一个错误,因为该行为是实现定义的。

于 2014-10-27T19:12:27.650 回答
10

根据C# Language Specification 5.0的第 7.8.2 节,我们有以下情况:

7.8.2 除法运算符
对于 x / y 形式的运算,应用二元运算符重载决议(第 7.3.4 节)来选择特定的运算符实现。操作数转换为所选运算符的参数类型,结果类型为运算符的返回类型。下面列出了预定义的除法运算符。运算符都计算 x 和 y 的商。

  • 整数除法:
    int operator /(int x, int y);
    uint operator /(uint x, uint y);
    long operator /(long x, long y);
    ulong operator /(ulong x, ulong y);
    如果右操作数的值为零,System.DivideByZeroException则抛出 a。除法将结果四舍五入到零。因此,结果的绝对值是小于或等于两个操作数的商的绝对值的最大可能整数。当两个操作数具有相同符号时,结果为零或正,当两个操作数具有相反符号时,结果为零或负。如果左操作数是可表示的最小 int 或 long 值,而右操作数是 –1,则会发生溢出。在检查的上下文中,这会导致System.ArithmeticException抛出一个(或其子类)。在未经检查的上下文中,实现定义是否System.ArithmeticException(或其子类)被抛出或溢出未报告,结果值为左操作数的值。
于 2014-10-27T19:22:10.380 回答