表达式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>
为什么规范会保留结果实现定义?以下是反对这样做的理由:
idiv
指令总是会导致异常。同样有趣的是,如果x / y
是编译时常量,我们确实得到unchecked(int.MinValue / -1) == int.MinValue
:
Console.WriteLine(unchecked(int.MinValue / -1)); //-2147483648
这意味着x / y
可能会有不同的行为,具体取决于所使用的语法形式(而且不仅取决于x
和y
的值)。这是规范允许的,但它似乎是一个不明智的选择。为什么C#设计得像这样?
A similar question指出规范中规定了这种确切行为的位置,但它没有(足够)回答为什么语言是这样设计的。不讨论替代选择。
答案 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是很方便的,但是溢出行为实际上是预期和期望的。通过检查实现说明,程序员可以确定此行为是否实际上是预期的。