Floating point numbers and string representations

I’ve recently been doing some work on the HealthVault SDK to improve the consistency of how it deals with floating-point numbers, and thought that the information would be of general interest.

First off, I’ll note that if you’re new to the often-surprising world of floating-point arithmetic, a few minutes reading What Every Computer Scientist Should Know About Floating-Point Arithmetic would be a good introduction.

While my code examples are C#, this is a general issue, not just a .NET one.

Anyway, off to the issue. Consider the following:

static void Main(string[] args)
{
double original = 3.1;
string stringRepresentation = original.ToString();
double parsed = Double.Parse(stringRepresentation);

    Console.WriteLine("Equal: " + (original == parsed).ToString());
}

What is the output of this program?

If you said “Equal: true”, you are correct. You might try a few more numbers before deciding that this is a general solution. Or maybe you don’t even think about it..

But what if you choose a different number:

double original = 667345.67232599994;

What is the output this time?

It is “false”.

I hope that there was sufficient foreshadowing earlier in the post so that you are not uncomfortably disturbed by this turn of events.

We could modify our code to check whether the two numbers are equal within an epsilon value. Or, we could dig a bit deeper…

If we look at the wikipedia, we’ll find that under the IEEE 745 standard for floating-point arithmetic, there are 53 significant bits in the fractional part of the number. That is not quite 16 digits, so to be correct, we treat the numbers as if they only have 15 digits of precision when we convert them to strings. The last few bits are ignored.

Another way of saying that is to say that it is possible to find two numbers that have the same ToString() representation but are different in those last few bits. Which is what is going on her.

If we are printing out numbers for somebody to look at, the ToString() behavior is what we want, because the numbers really only have 15 digits of precision.

In this scenario, however, what we want is a string format that will ensure that we get the exact same number back.  We can do that by using the “R” numeric format:

string stringRepresentation = original.ToString("R");

That gets us the behavior that we want. We could also have called XmlConvert.ToString(), which has the same behavior.