Double.MaxValue到整数是否定的?

时间:2014-03-31 08:48:33

标签: c# .net casting type-conversion

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

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

我理解编译器错误,如果它在运行时太大或OverflowException,或者如果我使用unchecked转换可能不会引发异常,但结果变得不确定和不正确(否定)。

同样奇怪的是,值为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,我会得到一个{{1}似乎迈克尔和巴雷的答案在头上敲了一下。 }:

OverflowException

3 个答案:

答案 0 :(得分:14)

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没有发挥作用(仅涉及整体操作)。

答案 1 :(得分:8)

可能不是一个完整的答案,但C#语言规范(§6.2.1)说明了这一点:

  

在未经检查的上下文中,转换始终成功,并且   进展如下。

     

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

     

•否则,源操作数向四舍五入   零到最接近的整数值。如果这个积分值在   目的地类型的范围,然后这个值是的结果   转换。

     

•否则,转换结果为 a   目的地类型的未指定值

(强调我的)。

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

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

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

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,在此实现中。

答案 2 :(得分:2)

此处的默认行为似乎是unchecked,即除非您明确指定checked,否则溢出不会被检测到:

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

事后看来,尝试从double直接转换为long而不首先验证或约束输入是不明智的,因为有两个方面:

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

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

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

由于这在内部对checked进行了检查,并对四舍五入采取了意见,即:

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