为什么定义C#规范(int.MinValue / -1)实现?

时间:2015-08-02 18:14:39

标签: c# language-design

表达式int.Minvalue / -1根据C#规范导致实现定义的行为:

  

7.8.2分部运营商

     

如果左操作数是最小的可表示的int或long值而右操作数是-1,则为   发生溢出。在检查的上下文中,这会导致a   要抛出的System.ArithmeticException(或其子类)。在一个   未经检查的上下文,是实现定义至于是否   抛出System.ArithmeticException(或其子类)或   溢出没有报告,结果值是   左操作数。

测试程序:

var x = int.MinValue;
var y = -1;
Console.WriteLine(unchecked(x / y));

这会在.NET 4.5 32位上引发OverflowException,但它没有。{/ p>

为什么规范会保留结果实现定义?以下是反对这样做的理由:

  1. 在这种情况下,x86 idiv指令总是会导致异常。
  2. 在其他平台上,可能需要运行时检查来模拟它。但是与该部门的成本相比,该检查的成本会很低。整数除法非常昂贵(15-30个周期)。
  3. 这会打开兼容性风险("写一次无处运行")。
  4. 开发人员惊喜。
  5. 同样有趣的是,如果x / y是编译时常量,我们确实得到unchecked(int.MinValue / -1) == int.MinValue

    Console.WriteLine(unchecked(int.MinValue / -1)); //-2147483648
    

    这意味着x / y可能会有不同的行为,具体取决于所使用的语法形式(而且不仅取决于xy的值)。这是规范允许的,但它似乎是一个不明智的选择。为什么C#设计得像这样?

    A similar question指出规范中规定了这种确切行为的位置,但它没有(足够)回答为什么语言是这样设计的。不讨论替代选择。

2 个答案:

答案 0 :(得分:10)

这是C#语言规范的更大兄弟Ecma-335,即公共语言基础结构规范的副作用。第III节第3.31章描述了DIV操作码的作用。 C#规范通常必须遵循的规范,这是不可避免的。它指定可能抛出但不要求它。

否则就真实处理器的实际情况进行评估。每个人都使用的那个是奇怪的。英特尔处理器对溢出行为过于古怪,它们是在20世纪70年代设计的,假设每个人都会使用INTO指令。没有人,另一天的故事。它不会忽略IDIV上的溢出然后提升#DE陷阱,不能忽略那个响亮的爆炸声。

在不一致的处理器行为之上,在一个粗略的运行时规范之上编写语言规范非常困难。很少有C#团队可以做到这一点,但转发不精确的语言。他们已经超越了规范,记录了OverflowException而不是ArithmeticException。很调皮。他们偷看了。

偷看了这种做法。这不太可能是一个问题,抖动决定是否内联。并且non-inlined version抛出,期望内联版本也是如此。没有人感到失望。

答案 1 :(得分:7)

C#的主要设计目标据说是#34;最小惊喜法&#34;。根据该指南,编译器不应该试图猜测程序员的意图,而应该向程序员发出信号,要求正确指定意图需要额外的指导。这适用于感兴趣的情况,因为在two's-complement arithmetic的限制内,操作会产生非常令人惊讶的结果: Int32.MinValue / -1 评估为 Int32.MinValue < / em>的。发生了溢出,并且需要一个0的不可用的33位来正确表示 Int32.MaxValue + 1 的正确值。

正如预期的那样,并在您的引用中注明,在检查的上下文中,会引发异常以提醒程序员未能正确指定意图。在未经检查的上下文中,允许实现在检查的上下文中行为,或允许溢出并返回令人惊讶的结果。有一些上下文,比如bit-twiddling,在这种情况下使用signed int是很方便的,但是溢出行为实际上是预期和期望的。通过检查实现说明,程序员可以确定此行为是否实际上是预期的