Why does Double.MaxValue casted to an integral type results in a negative value, the smallest value of that type?
double maxDouble = double.MaxValue; /
Maybe not a full answer, but the C# Language Specification (§6.2.1) says this:
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.
(emphasis mine).
(Michael Burr answered at the same time as me, and he also included info on the default checked
/unchecked
context in C#, cf. comments below, so this answer is largely redundant now.)
Edit 1: Note that if the conversion is done compile-time (constant expression conversion), rules are a bit different. Try modifying your maxDouble
variable with the const
modifier. The C# compiler will then be able to see the values, and it will require you to be explicit about the unchecked
.
Edit 2: In my version of the runtime (.NET 4.5 for Windows 8.1), the following code:
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);
gives:
-9223372036854775808 -9223372036854775808 -9223372036854775808 -9223372036854775808 -9223372036854775808 -9223372036854775808 -9223372036854775808
so it appears that the "unspecified value" is in fact "always" MinValue
of the destination type, in this implementation.
It seems the default behaviour here is unchecked
, viz that unless you explicitly specify checked
, the overflow goes undetected:
double maxDouble = double.MaxValue; // 1.7976931348623157E+308
long uncheckedMaxDoubleLong = (long)maxDouble; // -9223372036854775808
long checkedMaxDoubleLong = checked((long)maxDouble); // ** Overflow Exception
In hindsight, attempting direct conversion from double
to long
without validating or constraining the input first is ill advised due to 2 aspects:
So, a better bet here may have been to use Convert.ToInt64
:
var convertedDouble = Convert.ToInt64(maxDouble); // ** OverflowException
As this internally does the checked
checking for you, and takes an opinion on rounding, viz:
return checked((long)Math.Round(value));
The C# Language Specification (Version 5.0) says the following in 6.2.1 "Explicit numeric conversions" (emphasis added):
For a conversion from float or double to an integral type, the processing depends on the overflow checking context (§7.6.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.
And in 7.6.12 "The checked and unchecked operators"
For non-constant expressions (expressions that are evaluated at run-time) that are not enclosed by any checked or unchecked operators or statements, the default overflow checking context is unchecked unless external factors (such as compiler switches and execution environment configuration) call for checked evaluation.
For conversions from double
to decimal
: "If the source value is NaN, infinity, or too large to represent as a decimal, a System.OverflowException is thrown". checked
vs unchecked
doesn't come into play (those deal with integral operations only).