为什么这个检查计算不会抛出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
并返回大数时会发生什么,还有decimal
和double
很有趣:)(字节表示是唯一的表示你可以信任btw,在double
时不要使用调试器的精度)