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? #24892

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

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

LeonidVasilyev opened this issue Feb 2, 2018 · 4 comments

Comments

@LeonidVasilyev
Copy link

@LeonidVasilyev LeonidVasilyev 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
Copy link
Member

@joperezr joperezr commented Feb 27, 2018

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

Loading

@tannergooding
Copy link
Member

@tannergooding tannergooding 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).

Loading

@mairaw
Copy link
Contributor

@mairaw mairaw commented Feb 27, 2018

Adding @rpetrusha

Loading

@tannergooding
Copy link
Member

@tannergooding tannergooding 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..

Loading

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 3.0 milestone Jan 31, 2020
@msftbot msftbot bot locked as resolved and limited conversation to collaborators Dec 18, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants