Question

I discovered this odd behavior. Then I tried a few experiments on the Immediate window of Visual Studio...

? Convert.ToSingle(Decimal.MinValue)
-7.92281625E+28

No surprises here.

? Convert.ToDecimal(Convert.ToSingle(Decimal.MinValue))
'Convert.ToDecimal(Convert.ToSingle(Decimal.MinValue))' threw an exception of type 'System.OverflowException'
    base: {"Value was either too large or too small for a Decimal."}

First odd behavior: even if Decimal.MinValue is at the boundary of the Decimal domain, I thought that converting it to float wouldn't push it over the domain limit.

? Convert.ToDecimal(-7.92281625E+28)
-79228162500000000000000000000

Uhm... weird... oh not so weird: it is considering the expression as a double.

? Convert.ToDecimal(-7.92281625E+28F)
'Convert.ToDecimal(-7.92281625E+28F)' threw an exception of type 'System.OverflowException'
    base: {"Value was either too large or too small for a Decimal."}

Let's try this....

? Convert.ToDecimal(-7.92281625E+28D)
-79228162500000000000000000000

... okay. As I thought, it can't convert that number if it is considering it a float, but it has no problem in converting it back to decimal when considering it a double.

? Convert.ToDecimal(Convert.ToDouble(Decimal.MinValue))
'Convert.ToDecimal(Convert.ToDouble(Decimal.MinValue))' threw an exception of type 'System.OverflowException'
    base: {"Value was either too large or too small for a Decimal."}

Wait! What!? Now I'm really lost. What's the difference between the last two expressions? And why does it fail to convert back from float?

Was it helpful?

Solution

You can check how exactly your value is represented within double variable using code posted by Eric Lipper on his blog: Looking inside a double

For your Convert.ToDouble(decimal.MinValue) it prints following info:

Raw sign: 1
Raw exponent: 10001011111
Raw mantissa: 0000000000000000000000000000000000000000000000000000
Normal
Sign: -
Exponent: 96
Exact binary fraction: 1.0000000000000000000000000000000000000000000000000000
Nearest approximate decimal: -7,92281625142643E+28
Exact rational fraction: -79228162514264337593543950336
Exact decimal fraction: -79228162514264337593543950336

Exact decimal fraction is the most important part here.

exact decimal fraction: -79,228,162,514,264,337,593,543,950,336
decimal.MinValue:       -79,228,162,514,264,337,593,543,950,335

As you can see, it's slightly lower than decimal.MinValue, and that's why you're getting an exception.

The same case applies to Convert.ToSingle(Convert.ToSingle(decimal.MinValue)).

OTHER TIPS

The types Single and Double doesn't represent all values exactly, like the Decimal type does.

The Double literal -7.92281625E+28 doesn't end up as a Double that contains that value exactly, only the closest value that can be represented by that type. Most literal values are adjusted slightly upwards or downwards, as the storage in the floating point types are based on binary numbers, not decimal numbers.

When you convert Decimal.MinValue to a Single, it will be adjusted slightly downwards, which means that it gets just out of the range that can be represented by a Decimal. When you try to convert it back to Decimal that will fail, because it's out of range. The conversion only works if the number is within the range of a Decimal, not when it is within the range that a Decimal can be converted to.

The double representation of Decimal.MinValue is not exact and is a bit smaller than Decimal.MinValue causing the overflow.

VS 2012 Immediate window gives the following results which indicate this a little better.

?Decimal.MinValue
-79228162514264337593543950335
?Convert.ToDouble(Decimal.MinValue)
-7.9228162514264338E+28

Convert.ToDouble is...

-79228162514264338000000000000

Notice the last digit is an 8 vs 7 in Decimal.MinValue which makes it too small to fit into a Decimal.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top