Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In what cases "R" format fails to round-trip float value? #26785

Closed
LeonidVasilyev opened this issue Feb 2, 2018 · 4 comments

Comments

@LeonidVasilyev
Copy link

commented Feb 2, 2018

Standard Numeric Format Strings article states that in order to successfully round-trip Single value one must use G9 format specifier because R doesn't work sometimes:

When used with a Single value, the "G9" format specifier ensures that the original Single value successfully round-trips. This is because Single is an IEEE 754-2008-compliant single-precision (binary32) floating point number that gives up to nine significant digits of precision. We recommend its use instead of the "R" format specifier, since in some cases "R" fails to successfully round-trip single-precision floating point values.

For Double and Single values, the "R" format specifier in some cases fails to successfully round-trip the original value and also offers relatively poor performance. Instead, we recommend that you use the "G17" format specifier for Double values and the "G9" format specifier to successfully round-trip Single values.

To find such cases I used code snippet similar to the following one (Windows 10 x64, .NET Core 2.0 and .NET Framework 4.5) and found none:

using System;
using System.Globalization;

public class Program
{
    static unsafe float Int32BitsToSingle(int value) => *(float*)(&value);
    static void Main(string[] args)
    {
        long cnt = 0;
        long expCnt = (long)int.MaxValue - int.MinValue + 1;
        var inv = CultureInfo.InvariantCulture;
        for (int i = 0; cnt < expCnt; ++i, ++cnt)
        {
            float f = Int32BitsToSingle(i);
            if (cnt % 1000000 == 0)
            {
                Console.Clear();
                Console.WriteLine(((float)cnt / expCnt).ToString("#0.####%", inv));
            }
            if (float.IsNaN(f))
                continue;
            var r = f.ToString("R", inv);
            if (float.Parse(r, inv) == f)
                continue;

            Console.WriteLine("Found on " + i);
            Console.WriteLine(f.ToString(inv));
            Console.WriteLine(f.ToString("G9", inv));
            Console.WriteLine(r);
            return;
        }
        Console.WriteLine("Not found");
    }
}

@joperezr joperezr added this to the Future milestone Feb 27, 2018

@joperezr

This comment has been minimized.

Copy link
Member

commented Feb 27, 2018

cc: @mairaw Given that this is official documentation, do you mind sharing an example of where does this fail?

@tannergooding

This comment has been minimized.

Copy link
Member

commented Feb 27, 2018

@LeonidVasilyev, this can vary from platform to platform as the code in question previously depended on native APIs to complete the operation.

There was some work to fix these issues and they should generally be resolved. However, explicitly requesting G9 or G17 will still be more performent than R due to the way it is implemented (it first tries to convert to a 7/15 digit string, round trips it back to a float/double, and returns the result if equal to the original; otherwise it recomputes at 9/17 digits).

@mairaw

This comment has been minimized.

Copy link
Contributor

commented Feb 27, 2018

Adding @rpetrusha

@tannergooding

This comment has been minimized.

Copy link
Member

commented Feb 1, 2019

Now that dotnet/coreclr#22040 is merged, for .NET Core 3.0, ToString(), ToString("G"), ToString("R"), double.ToString("G17"), and float.ToString("G9") should now always produce a string that will roundtrip to the original value..

@karelz karelz modified the milestones: Future, 3.0 Apr 1, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.