Why does casting a NaN to a long yield a valid result?
In the sample c开发者_运维百科ode below I am dividing by zero which when I step through it with the debugger the (dividend / divisor) yields an Infinity or NaN (if the divisor is zero). When I cast this result to a long I get a valid result, usually something like -9223372036854775808. Why is this cast valid? Why doesn't it stop executing (throw an exception for example) rather than assign an arbitrary value?
double divisor = 0;
double dividend = 7;
long result = (long)(dividend / divisor);
Why is this cast valid?
A cast is valid if it is known at compile time that the conversion might succeed or always succeeds. Casts are only illegal when the conversion cannot possibly succeed. (For example, casting a sealed type to an interface it does not implement.) A conversion from double to long might succeed. Therefore the cast is valid.
Why doesn't it stop executing (throw an exception for example) rather than assign an arbitrary value?
Because you didn't ask for an exception! The spec is extremely clear on what the expected behaviour is. See section 6.2.1:
For a conversion from float or double to an integral type, the processing depends on the overflow checking context in which the conversion takes place:
In a checked context, the conversion proceeds as follows:
• If the value of the operand is NaN or infinite, a System.OverflowException is thrown.
[...]
In an unchecked context, the conversion always succeeds, and proceeds as follows.
• If the value of the operand is NaN or infinite, the result of the conversion is an unspecified value of the destination type.
You're executing the code in an unchecked context; you asked for no exception so you're getting no exception. If you want an exception, ask for one; use a checked context.
By default, C# arithmetic is unchecked, so invalid operations will not throw exceptions.
You can use a checked
block to force the runtime to check for overflow and throw exceptions, like this:
checked {
double divisor = 0;
double dividend = 7;
long result = (long)(dividend / divisor);
}
Note that there will be a slight performance penalty.
The behavior is explicitly documented in the C# language specification, section 6.2.1:
For a conversion from float or double to an integral type, the processing depends on the overflow checking context (§7.5.12) in which the conversion takes place:
In a checked context, the conversion proceeds as follows:
- If the value of the operand is NaN or infinite, a System.OverflowException is thrown.
- Otherwise, the source operand is rounded towards zero to the nearest integral value. If this integral value is within the range of the destination type then this value is the result of the conversion.
- Otherwise, a System.OverflowException is thrown.
In an unchecked context, the conversion always succeeds, and proceeds as follows.
- If the value of the operand is NaN or infinite, the result of the conversion is an unspecified value of the destination type.
- Otherwise, the source operand is rounded towards zero to the nearest integral value. If this integral value is within the range of the destination type then this value is the result of the conversion.
- Otherwise, the result of the conversion is an unspecified value of the destination type.
Bold added for emphasis. You've got Infinity and an unchecked context. The value you get is unspecified. Use the checked keyword to make it bomb.
You can also use the "checked" flag when compiling, which will work the same way as wrapping all of your code in a checked-block.
It depends on the data type. Different data types will have different results for divide by zero.
http://blogs.msdn.com/rafats/archive/2006/07/20/673337.aspx
and
http://www.eggheadcafe.com/software/aspnet/30920566/divide-by-zero-question.aspx
From Jon Skeet on a different site (the above link):
In a word, standards. At least, I suspect that's the reason. float/double follow the IEC 60559 standard rules for arithmetic, including division by zero resulting in an "infinite" value rather than throwing an exception.
Decimal doesn't (or at least doesn't have to - the C# spec allows for the possibility) support an "infinite" value whereas float/double do. Similar decimal doesn't have a NaN specified.
Divide by Zero explanation: http://www.philforhumanity.com/How_to_Divide_by_Zero.html
精彩评论