Mark
Mark

Reputation: 150

c# and floating point inaccuracies

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

Answers (2)

Jeppe Stig Nielsen
Jeppe Stig Nielsen

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

Jeff
Jeff

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

Related Questions