Runtime: Change in the default output of double.ToString()

Created on 3 Apr 2019  路  10Comments  路  Source: dotnet/runtime

When working with a friend, we noticed that a number of tests failed when running on .net core 3.0 that passed on .net 2.2.

The test is simple

double x1 = 10000101;
double p1 = 1e-5;

double x2 = 100001010;
double p2 = 1e-6;
Debug.Assert((x1 * p1).ToString() == (x2 * p2).ToString());

On .net core 2.2 both ToString statements print 100.00101 on 3.0 it prints 100.00101000000001

This causes a large display problem in output files.

area-System.Numerics question

Most helpful comment

I just see a lot of trailing numbers in web pages and UI in the near future

Yes; I agree there will likely be an increase in that for some programs. But, rolling forward to a new major version is not the default and my hope is this will drive applications to use the appropriate format specifiers to ensure it is consistent/correct across all frameworks/runtimes.

It might be worth noting that x86 vs x64 vs ARM vs ARM64 and Windows vs Linux vs OSX (and all combinations there-off) could have been returning different results for ToString() or Parse (and we had multiple bugs filed around these differences and the lack of roundtrip working correctly). The implementations on .NET Core 3.0 and forward are now consistent and IEEE compliant, so the only remaining concern should be for people moving from an older version of the runtime to the latest.

All 10 comments

This is largely covered in my blog post here: https://devblogs.microsoft.com/dotnet/floating-point-parsing-and-formatting-improvements-in-net-core-3-0/

.NET Core 3.0 was updated to be IEEE compliant and ensure that the default behavior of ToString is that it produces a roundtrippable string.

If you are wanting a "pretty string" you can pass in a standard or custom numeric format specifier to ensure that the string only has the appropriate number of digits for you.

Luckily on this particular project we have a large number of unit tests catching it. I can see a lot of potential issues and tbh I know of some I need to go and fix before a move to .net 3.0

For instance all objects serialized to Json now get this new string format sent out to clients via ASP.NET

For instance all objects serialized to Json now get this new string format sent out to clients via ASP.NET

The format isn't new; it's just that the result is now guaranteed to contain the minimum number of digits required to roundtrip the result back to the original value.

Many serializers (including JSON.NET, to my knowledge; and from locally testing) are already explicitly doing "G17" to workaround strings not roundtripping by default in prior releases.

Take for example Math.PI. Math.PI.ToString() used to return 3.14159265358979, this value when passed back into double.Parse() would return a value such that (result == Math.PI) == false. Now, we correctly return 3.1415926535897931 which roundtrips back such that (resullt == Math.PI) == true.

I guess my point is this, if you need round tripping (and we often do) we understand that, and set the formatting explicitly. We also have plenty of tests around it because the numerical accuracy is important.

However there are plenty of places for display that .ToString() is done and a lot less testing around those numbers, I just see a lot of trailing numbers in web pages and UI in the near future :)

I just see a lot of trailing numbers in web pages and UI in the near future

Yes; I agree there will likely be an increase in that for some programs. But, rolling forward to a new major version is not the default and my hope is this will drive applications to use the appropriate format specifiers to ensure it is consistent/correct across all frameworks/runtimes.

It might be worth noting that x86 vs x64 vs ARM vs ARM64 and Windows vs Linux vs OSX (and all combinations there-off) could have been returning different results for ToString() or Parse (and we had multiple bugs filed around these differences and the lack of roundtrip working correctly). The implementations on .NET Core 3.0 and forward are now consistent and IEEE compliant, so the only remaining concern should be for people moving from an older version of the runtime to the latest.

o the only remaining concern should be for people moving from an older version of the runtime to the latest.

And presumably the parallel track of .net framework people

And presumably the parallel track of .net framework people

Yes, but that should already be a concern for them, since x86 vs x64 were already not producing consistent results.

After sitting down with a bunch of of our quants, we are cool with the change. Good blog post and good work 馃憤. Off to check our UI's now ;)

Does this change also cover (-0.1M).ToString("0") evaluating as -0 when it was just 0 before Core 3?

Was this page helpful?
0 / 5 - 0 ratings