In the sample code 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With