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");
}
}
cc: @mairaw Given that this is official documentation, do you mind sharing an example of where does this fail?
@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).
Adding @rpetrusha
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..
Most helpful comment
Now that dotnet/coreclr#22040 is merged, for .NET Core 3.0,
ToString(),ToString("G"),ToString("R"),double.ToString("G17"), andfloat.ToString("G9")should now always produce a string that will roundtrip to the original value..