Search code examples
c#floating-pointdoubleieee

Converting double to string in .NET isn't as precise as C++?


The following C++ program expectedly outputs 0.300000000000000040000:

cout << setprecision(40) << (0.1 + 0.2) << endl;

However the following C# code:

System.Console.WriteLine("{0:F40}", 0.1+0.2);

... unexpetedly outputs 0.3000000000000000000000000000000000000000, while it (expectedly) evaluates 0.1+0.2 == 0.3 as False.

Where is the '4' and how can I output it?


Solution

  • What do you get if you say

    System.Console.WriteLine("{0:R}", 0.1+0.2);
    

    A format string like "F40" will only add zeros to the end of the string. It will not give you extra precision compared to just "G". On the other hand, "R", will reveal two extra digits (or one if the last of the two digits extra is 0) if (and only if) this is necessary to distinguish the number from a number closer to the string from "G".

    Addition: To get a better understanding of how the format strings work in .NET, try running the following code:

    double a = BitConverter.ToDouble(new byte[] { 49, 51, 51, 51, 51, 51, 211, 63, }, 0);
    double b = BitConverter.ToDouble(new byte[] { 50, 51, 51, 51, 51, 51, 211, 63, }, 0);
    double c = BitConverter.ToDouble(new byte[] { 51, 51, 51, 51, 51, 51, 211, 63, }, 0);
    double d = BitConverter.ToDouble(new byte[] { 52, 51, 51, 51, 51, 51, 211, 63, }, 0);
    double e = BitConverter.ToDouble(new byte[] { 53, 51, 51, 51, 51, 51, 211, 63, }, 0);
    
    Console.WriteLine("using G:");
    Console.WriteLine(a.ToString());
    Console.WriteLine(b.ToString());
    Console.WriteLine(c.ToString());
    Console.WriteLine(d.ToString());
    Console.WriteLine(e.ToString());
    Console.WriteLine("using F40:");
    Console.WriteLine(a.ToString("F40"));
    Console.WriteLine(b.ToString("F40"));
    Console.WriteLine(c.ToString("F40"));
    Console.WriteLine(d.ToString("F40"));
    Console.WriteLine(e.ToString("F40"));
    Console.WriteLine("using R:");
    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"));
    

    Here, a, b, c, d, and e are "neighbors" as System.Doubles which is evident from the byte arrays. You should see that the "R" format string gives extra digits only in case of a, b, and d, e. For c, no extra digits are needed, but it is clear that if one were to write c with 17 figures, the last two of the 17 wouldn't be 00 (see also Jon Skeet's answer for the full decimal expansions which the methods built into .NET won't give you easily).