Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't conversion of Decimal.MaxValue between Single and Decimal transitive?

Tags:

c#

If I try and convert Decimal.MaxValue from Decimal to Single and back again, the conversion fails with an OverflowException:

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

What gives? Surely the value of Decimal.MaxValue as a Single should be a valid Decimal value?

I understand that the differences between Single and Decimal and expect a loss of precision converting from Decimal to Single but as Single.MaxValue is greater than Decimal.MaxValue it doesn't make sense that the "Value was either too large or too small for Decimal". If you think that does make sense please explain why in an answer.

Additionally,

Convert.ToSingle(Decimal.MaxValue)
// 7.92281625E+28

so there is no problem converting this number to a Single.

like image 543
simonwo Avatar asked Nov 19 '25 16:11

simonwo


1 Answers

You're making an incorrect assumption:

Surely the value of Decimal.MaxValue as a Single should be a valid Decimal value?

The value of Decimal.MaxValue is 79,228,162,514,264,337,593,543,950,335. A float can't represent anywhere near that level of precision, so when you convert the decimal value to a float you end up with an approximation.

In this case the float is represented internally as 2^96. This turns out to be a pretty good approximation -- 79,228,162,514,264,337,593,543,950,336 -- but take note of that last digit.

The value of the float is larger than Decimal.MaxValue, which is why your attempt to convert it back into a decimal fails.

(The .NET framework doesn't offer much help when trying to diagnose these kind of problems. It'll always display a pre-rounded value for the float -- 7.92281625E+28 or similar -- and never the full approximation that it's using internally.)

like image 101
LukeH Avatar answered Nov 21 '25 05:11

LukeH