decimal oddity

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

Hi,

this is not the usual why-can't-I-represent-0.1-in-double post.

I don't understand why we loose information when converting from double to
decimal.

Consider the following code. d1 and d2 are clearly different.

When converting to decimal, I expect dec1 and dec2 to be different too.
decimal tries to be smart and rounds 0.082000000000000017 to 0.082
although it is possible to represent 0.082000000000000003 exactly in decimal.

Is this documented somewhere?


double d1 = 0.082000000000000003;
double d2 = 0.082000000000000017;
long l = 82000000000000017;

System.Diagnostics.Trace.WriteLine(d1.ToString("G17")); //
0.082000000000000003
System.Diagnostics.Trace.WriteLine(d2.ToString("G17")); //
0.082000000000000017

decimal dec1 = (decimal)(d1);
decimal dec2 = (decimal)(d2);
decimal dec3 = (decimal)(l) / 1e18m;
decimal dec4 = decimal.Parse(d2.ToString("G17"));
System.Diagnostics.Trace.WriteLine(dec1.ToString("G17")); // 0.082
System.Diagnostics.Trace.WriteLine(dec2.ToString("G17")); // 0.082
System.Diagnostics.Trace.WriteLine(dec3.ToString("G17")); //
0.082000000000000017
System.Diagnostics.Trace.WriteLine(dec4.ToString("G17")); //
0.082000000000000017

TIA
Henrik
 
this is not the usual why-can't-I-represent-0.1-in-double post.

I don't understand why we loose information when converting from double to
decimal.

Because not every double is exactly representable as a decimal. In
particular, doubles can be much, much bigger than decimals or much,
much smaller. Also, the *exact* value represented by a double can have
an awful lot of decimal places (as shown below). That exact value
can't always be represented as a decimal.
Consider the following code. d1 and d2 are clearly different.

When converting to decimal, I expect dec1 and dec2 to be different too.
decimal tries to be smart and rounds 0.082000000000000017 to 0.082
although it is possible to represent 0.082000000000000003 exactly in decimal.

d1 isn't exactly 0.082000000000000003 though - it's actually
0.08200000000000000344169137633798527531325817108154296875

Likewise, d2 isn't exactly 0.082000000000000017 - it's actually
0.082000000000000017319479184152442030608654022216796875

Now, the conversion from double to decimal is only specified in the
following way (according to the C# spec):

<quote>
For a conversion from float or double to decimal, the source value is
converted to decimal representation and rounded to the nearest number
after the 28th decimal place if required (§4.1.7). If the source value
is too small to represent as a decimal, the result becomes zero. If
the source value is NaN, infinity, or too large to represent as a
decimal, a System.OverflowException is thrown.
</quote>

Further, consider that double values are only deemed to be "trusted"
to 15 significant digits: "By default, a Double value contains 15
decimal digits of precision, although a maximum of 17 digits is
maintained internally."

If you look at the first 15 significant digits of the exact decimal
representation of your doubles, they are the same. I believe that's
what is being used for the double to decimal conversion, on the
grounds that any further digits are effectively noise - they're
present but are effectively artefacts of the representation.

Does that make any sense?

Jon
 
Thank you, Jon,

but...
Because not every double is exactly representable as a decimal. In
particular, doubles can be much, much bigger than decimals or much,
much smaller. Also, the *exact* value represented by a double can have
an awful lot of decimal places (as shown below). That exact value
can't always be represented as a decimal. agreed.

d1 isn't exactly 0.082000000000000003 though - it's actually
0.08200000000000000344169137633798527531325817108154296875
OK, but 0.082000000000000003 is representable in decimal and it is closer to
d1 than 0.082.
Likewise, d2 isn't exactly 0.082000000000000017 - it's actually
0.082000000000000017319479184152442030608654022216796875
same here. 0.082000000000000017 is representable in decimal. It is closer to
d2 (rounded to 17 significant places or not) than 0.082.
Now, the conversion from double to decimal is only specified in the
following way (according to the C# spec):

<quote>
For a conversion from float or double to decimal, the source value is
converted to decimal representation and rounded to the nearest number
after the 28th decimal place if required ('4.1.7). If the source value
But in fact it looks as if it is rounded after the 15th decimal place.
Further, consider that double values are only deemed to be "trusted"
to 15 significant digits: "By default, a Double value contains 15
decimal digits of precision, although a maximum of 17 digits is
maintained internally."
This may be the rationale for the behavior, but it's not clearly documented.
If you look at the first 15 significant digits of the exact decimal
representation of your doubles, they are the same. I believe that's
what is being used for the double to decimal conversion, on the
grounds that any further digits are effectively noise - they're
present but are effectively artefacts of the representation.

Does that make any sense?
Sure. Thank you.
 
On Nov 6, 12:01 pm, Henrik Schmid

But in fact it looks as if it is rounded after the 15th decimal place.

No, it's being rounded after the 15th significant digit. There's a big
difference. Consider this:
0.00000000000000000000123456789012345

That's only got 15 significant digits, but to be stored as a decimal
it needs to be rounded after the 28th decimal place.
This may be the rationale for the behavior, but it's not clearly documented.

True. Admittedly in some cases it's *good* not to have documented
behaviour - it allows for different implementations to do different
things to take advantage of particular conditions. I don't think I
agree with the lack of specification in this particular case though :(
Sure. Thank you.

My pleasure. If I remember, I'll try to pass on a request for
clarification to the C# spec team.

Jon
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top