Reputation: 150
I am trying to understand floating point inaccuracies and how to handle them in c#.
I have looked at Floating point inaccuracy examples which gives some good answers, but I want to understand it specifically to c#.
Using decimal '8.8', how would I convert this to a binary representation and then back to decimal so that that that value changes to '8.8000000000000007'?
I have tried using suggestions from How to get the IEEE 754 binary representation of a float in C# without luck.
Upvotes: 0
Views: 304
Reputation: 61952
You can compare some different doubles like this:
double a = BitConverter.ToDouble(new byte[] { 156, 153, 153, 153, 153, 153, 33, 64, }, 0);
double b = BitConverter.ToDouble(new byte[] { 155, 153, 153, 153, 153, 153, 33, 64, }, 0);
double c = BitConverter.ToDouble(new byte[] { 154, 153, 153, 153, 153, 153, 33, 64, }, 0);
double d = BitConverter.ToDouble(new byte[] { 153, 153, 153, 153, 153, 153, 33, 64, }, 0);
double e = BitConverter.ToDouble(new byte[] { 152, 153, 153, 153, 153, 153, 33, 64, }, 0);
Console.WriteLine(a.ToString("R"));
Console.WriteLine(b.ToString("R"));
Console.WriteLine(c.ToString("R"));
Console.WriteLine(d.ToString("R"));
Console.WriteLine(e.ToString("R"));
Using the format string "R"
shows extra digits but only if necessary to distinguish between other representable System.Double
.
Addition (explanation of the bytes): 64
and 33
give the sign, magnitude and first (most) significant bits of the number 8.8
. Since 8.8
is a fraction with a small denominator (the 5 in 44/5), it is not surprising that the rest of the bits repeat over and over with a short period. To get the exact 8.8
, the 153s would have to continue forever. But there's only room for eight bytes in a double
. Therefore we need to round. Rounding to 154
gives the closest value because the next "term" (153
) is closer to 256
than to 0
. Therefore c
is the most precise representation possible.
When you look through the output of the above code, you see that c
is just output as 8.8
even when we used the "R"
format string. But you know that c
is halfway between b
and d
(and also halfway between a
and e
), and from that you can easily estimate that the "true" decimal value most be very near 8.8000000000000007
.
Upvotes: 0
Reputation: 7674
Here's the thing, though: 8.8000000000000007 can't be exactly represented in double
, either. The closest value is 8.800000000000000710542735760100185871124267578125 (which I got from Jon Skeet's DoubleConverter). You could then use Decimal.Parse
on that string to get a decimal value of 8.80000000000000071054273576.
decimal d = 8.8M;
double dbl = (double)d;
string s = DoubleConverter.ToExactString(dbl);
decimal dnew = decimal.Parse(s);
Upvotes: 3