为什么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
答案 0 :(得分:14)
C#语言规范(5.0版)在6.2.1“显式数字转换”(重点补充)中说明以下内容:
对于从float或double到整数类型的转换,处理取决于溢出检查上下文(第7.6.12节),其中 转换发生:
在选中的上下文中,转换过程如下:
- 如果操作数的值为NaN或无限,则抛出System.OverflowException。
- 否则,源操作数向零舍入为最接近的整数值。如果该积分值在范围内 目的地类型,那么这个值就是转换的结果。
- 否则,抛出System.OverflowException。
在未经检查的上下文中,转换始终成功,并按以下步骤继续。
- 如果操作数的值为NaN或无穷大,则转换结果为目标类型的未指定值。
- 否则,源操作数向零舍入为最接近的整数值。如果该积分值在范围内 目的地类型,那么这个值就是转换的结果。
- 否则,转化结果是目的地类型的未指定值。
在7.6.12“已检查和未经检查的运算符”
对于非常量表达式(在 任何已检查或未检查的运算符未包含的运行时) 或语句,未选中默认溢出检查上下文 除非外部因素(如编译器切换和执行 环境配置)要求检查评估。
对于从double
到decimal
的转换:“如果源值为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));