为什么将int.MinValue除以-1会在未选中的上下文中抛出OverflowException?

时间:2014-10-27 18:46:07

标签: c# checked unchecked overflowexception

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

第二行会导致OverflowExceptionunchecked不应该阻止这个吗?

例如:

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

不会导致异常。

3 个答案:

答案 0 :(得分:33)

C#4规范的第7.72节(分部运营商)声明:

  

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

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

答案 1 :(得分:31)

这不是C#编译器或抖动可以控制的例外。它特定于Intel / AMD处理器,当IDIV指令失败时,CPU会生成#DE陷阱(Divide Error)。操作系统处理处理器陷阱并使用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的运行时支持不完善,它吞下异常并允许处理器重新启动除法指令。该程序挂起,在处理器不断生成#DE陷阱的情况下烧掉100%核心。将分裂转变为传奇的Halt and Catch Fire instruction:)

答案 2 :(得分: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。该部门将结果舍入为零。因此,结果的绝对值是小于或等于两个操作数的商的绝对值的最大可能整数。当两个操作数具有相同符号时,结果为零或正,当两个操作数具有相反符号时,结果为零或负。 如果左操作数是最小的可表示的int或long值而右操作数是-1 ,则发生溢出。在检查的上下文中,这会导致抛出System.ArithmeticException(或其子类)。 在未经检查的上下文中,它是实现定义的,是否抛出System.ArithmeticException(或其子类)或溢出未报告,结果值是左操作数的值。
  •