为什么这个检查计算不会抛出OverflowException?

有人可以解释以下行为:

static void Main(string[] args) { checked { double d = -1d + long.MinValue; //this resolves at runtime to -9223372036854780000.00 //long obviousOverflow = -9223372036854780000; //compile time error, '-' cannot be applied to operand of tpye ulong -> this makes it obvious that -9223372036854780000 overflows a long. double one = 1; long lMax = (long)(one + long.MaxValue); // THROWS long lMin = (long)(-one + long.MinValue); // THEN WHY DOES THIS NOT THROW? } } 

我没有说明为什么我在最后一行代码中没有得到OverFlowException

更新更新的代码使得显然检查完成时将double转换为long,除非在最后一种情况下。

您正在使用double -1d值( -1d )进行计算。 浮点数不会抛出.NET。 checked对他们没有任何影响。

但转换回long受到checked影响。 one + long.MaxValue不适合double的范围。 -one + long.MinValue确实适合该范围。 原因是有符号整数的负数多于正数。 long.MinValue没有long.MinValue等价物。 这就是为什么你的代码的负面版本适合并且正面版本不适合的原因。

添加操作不会改变任何内容:

 Debug.Assert((double)(1d + long.MaxValue) == (double)(0d + long.MaxValue)); Debug.Assert((double)(-1d + long.MinValue) == (double)(-0d + long.MinValue)); 

我们计算的数字超出了精确倍数的范围。 double可以精确地拟合整数高达2 ^ 53。 我们这里有舍入错误。 添加一个与添加零相同。 从本质上讲,你是在计算:

 var min = (long)(double)(long.MinValue); //does not overflow var max = (long)(double)(long.MaxValue); //overflows (compiler error) 

添加操作是红鲱鱼。 它没有改变任何东西。

显然,从双重到长期的转换有一些余地。 如果我们运行以下代码:

 checked { double longMinValue = long.MinValue; var i = 0; while (true) { long test = (long)(longMinValue - i); Console.WriteLine("Works for " + i++.ToString() + " => " + test.ToString()); } } 

在使用OverflowException Works for 1024 => -9223372036854775808失败之前,它上升到Works for 1024 => -9223372036854775808 ,并且-9223372036854775808值永远不会更改为i。

如果我们未选中运行代码,则不会抛出任何exception。

此行为与显式数字转换的文档不一致,该文档说明:

从double或float值转换为整数类型时,该值将被截断。 如果结果积分值超出目标值的范围,则结果取决于溢出检查上下文。 在已检查的上下文中,抛出OverflowException,而在未经检查的上下文中,结果是未指定的目标类型值。

但正如示例所示,截断不会立即发生。

我认为这是一个问题:

我没有说明为什么我在最后一行代码中没有得到OverFlowException

看看这一行(你使用的1d是无关紧要的,可以删除,它提供的唯一的东西是转换为double ):

 var max = (long)(double)long.MaxValue; 

它抛出因为最接近(我不知道规范,所以我不会进入什么“最接近”这里) int64.MaxValue双重表示大于最大的int64 ,它不能转换回来。

 var min = (long)(double)long.MinValue; 

另一方面,对于这一行,最接近的双重表示forms在int64.MinValue和0之间,因此可以将其转换回int64

我刚才所说的并不适用于所有抖动,硬件等组合,但我试图解释会发生什么。 请记住,在你的情况下,由于checked关键字而抛出它,没有它,抖动就会吞下它。

另外我建议你看一下BitConverter.GetBytes()来试验当你从double变为long并返回大数时会发生什么,还有decimaldouble很有趣:)(字节表示是唯一的表示你可以信任btw,在double时不要使用调试器的精度)