Double.MaxValue到整数是否定的?

为什么Double.MaxValue转换为整数类型会导致负值,该类型的最小值?

 double maxDouble = double.MaxValue; // 1.7976931348623157E+308 long maxDoubleLong = (long) maxDouble; // -9223372036854775808 

我理解编译器错误,如果它太大或运行时出现OverflowException ,或者如果我使用unchecked ,转换可能不会引发exception,但结果将变为未定义且不正确(否定)。

同样奇怪的是值很长long.MinValue

 bool sameAsLongMin = maxDoubleLong == long.MinValue; // true 

顺便说一句,如果我将它转换为int也会发生同样的情况:

 int maxDoubleInt = (int)maxDouble; // -2147483648 bool sameAsIntMin = maxDoubleInt == int.MinValue; // true 

如果它试图将其强制转换为decimal我会在运行时获得一个OverflowException

 decimal maxDoubleDec = (decimal)maxDouble; // nope 

更新 :看起来迈克尔和巴雷的答案在头上很明显,如果我明确地使用了checked我会得到一个OverflowException

 checked { double maxDouble = double.MaxValue; // 1.7976931348623157E+308 long maxDoubleLong = (long) maxDouble; // nope } 

C#语言规范(版本5.0)在6.2.1“显式数字转换”(重点添加)中说明以下内容:

  • 对于从float或double到整数类型的转换,处理取决于转换发生的溢出检查上下文(第7.6.12节):

    • 在已检查的上下文中,转换过程如下:

      • 如果操作数的值为NaN或无穷大,则抛出System.OverflowException。
      • 否则,源操作数向零舍入为最接近的整数值。 如果此整数值在目标类型的范围内,则此值是转换的结果。
      • 否则,抛出System.OverflowException。
    • 在未经检查的上下文中,转换始终成功,并按如下方式继续。

      • 如果操作数的值为NaN或无限,则转换的结果是目标类型的未指定值。
      • 否则,源操作数向零舍入为最接近的整数值。 如果此整数值在目标类型的范围内,则此值是转换的结果。
      • 否则,转换的结果是目标类型的未指定值。

并在7.6.12“已检查和未检查的运算符”

对于未由任何已检查或未检查的运算符或语句包含的非常量表达式(在运行时计算的表达式),除非外部因素(如编译器开关和执行环境配置)调用,否则将取消选中默认溢出检查上下文检查评估。

对于从doubledecimal转换:“如果源值为NaN,无穷大或太大而无法表示为十进制,则抛出System.OverflowException”。 checked vs unchecked不会发挥作用(仅处理整体操作)。

也许不是一个完整的答案,但C#语言规范(§6.2.1)说:

在未经检查的上下文中,转换始终成功,并按如下方式继续。

•如果操作数的值为NaN或无穷大,则转换结果为目标类型的未指定值。

•否则,源操作数将向零舍入为最接近的整数值。 如果此整数值在目标类型的范围内,则此值是转换的结果。

•否则,转换结果是目标类型的未指定值

(强调我的)。

(Michael Burr与我同时回答,他还在C#中包含了关于默认checked / unchecked上下文的信息,参见下面的评论,所以这个答案现在基本上是多余的。)

编辑1:请注意,如果转换完成编译时 (常量表达式转换),则规则略有不同。 尝试使用const修饰符修改maxDouble变量。 然后,C#编译器将能够看到这些值,并且它将要求您明确unchecked

编辑2:在我的运行时版本(.NET 4.5 for Windows 8.1)中,代码如下:

 double d1 = double.PositiveInfinity; double d2 = double.MaxValue; double d3 = 2.3e23; double d4 = double.NaN; double d5 = -2.3e23; double d6 = double.MinValue; double d7 = double.NegativeInfinity; Console.WriteLine((long)d1); Console.WriteLine((long)d2); Console.WriteLine((long)d3); Console.WriteLine((long)d4); Console.WriteLine((long)d5); Console.WriteLine((long)d6); Console.WriteLine((long)d7); 

得到:

  -9223372036854775808
 -9223372036854775808
 -9223372036854775808
 -9223372036854775808
 -9223372036854775808
 -9223372036854775808
 -9223372036854775808 

所以在这个实现中,似乎“未指定的值”实际上是“始终”目标类型的MinValue

看来这里的默认行为是unchecked ,即除非您明确指定checked ,否则溢出不会被检测到:

  double maxDouble = double.MaxValue; // 1.7976931348623157E+308 long uncheckedMaxDoubleLong = (long)maxDouble; // -9223372036854775808 long checkedMaxDoubleLong = checked((long)maxDouble); // ** Overflow Exception 

事后看来,由于以下两个方面,尝试直接从double转换为long而不首先validation或约束输入是不明智的:

  • 数值范围不匹配/溢出的可能性
  • 舍入考虑因素

所以,这里更好的选择可能是使用Convert.ToInt64

  var convertedDouble = Convert.ToInt64(maxDouble); // ** OverflowException 

因为这在内部对你checked检查,并对四舍五入进行了评论,即:

  return checked((long)Math.Round(value));