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

Significant double percision calculation/storing/parsing issue #59982

Closed
okarpov opened this issue Oct 5, 2021 · 13 comments
Closed

Significant double percision calculation/storing/parsing issue #59982

okarpov opened this issue Oct 5, 2021 · 13 comments
Labels
area-System.Numerics question Answer questions and provide assistance, not an issue with source code or documentation.
Milestone

Comments

@okarpov
Copy link

okarpov commented Oct 5, 2021

Description

I have found a significant issue with double precision types and calculations in .Net Core 5.0 (it seems that .Net Framework 4.8 has no such issue btw)

class Program
{
static void Main(string[] args)
{
double tpPrice = 41.59956901;
double coef = double.Parse("0.10000000");
Console.WriteLine(coef * 0.1);
double a = Math.Truncate(tpPrice * 0.33 / coef) * coef;
Console.WriteLine("Test: " + a + " ");
tpPrice -= a;
a = tpPrice * 0.5;
a = (Math.Truncate(a / coef) * coef);
tpPrice -= a;
Console.WriteLine("Test: " + a + " ");
a = (Math.Truncate(a / coef) * coef);
tpPrice -= a;
Console.WriteLine("Test: " + a + " ");
Console.ReadLine();
}
} 

gives the following results:

Test: 0.010000000000000002

Test: 13.700000000000001

Test: 13.9

Test: 13.9

Please, note the last 2 digit in first result line and last 1 digit in the second result line

I consider it as major issue as it will affect calculation results and lead to wrong string representations of double values that will lead to stop working in communications with 3rd party services.

Configuration

.Net 5.0 Console app
Windows 10 Pro
x64
(seems fine in .Net Framework 4.8)

Regression?

seems fine in .Net Framework 4.8

Other information

N/A

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Oct 5, 2021
@huoyaoyuan
Copy link
Member

in .Net Core 5.0 (it seems that .Net Framework 4.8 has no such issue btw)

.NET Core 3 modified floating point parsing/ToString to be compliant with latest IEEE754 standard. .NET Framework was not standard complaint, and should be considered "wrong".

@ghost
Copy link

ghost commented Oct 5, 2021

Tagging subscribers to this area: @dotnet/area-system-numerics
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

I have found a significant issue with double precision types and calculations in .Net Core 5.0 (it seems that .Net Framework 4.8 has no such issue btw)

class Program { static void Main(string[] args) { double tpPrice = 41.59956901; double coef = double.Parse("0.10000000"); Console.WriteLine(coef * 0.1); double a = Math.Truncate(tpPrice * 0.33 / coef) * coef; Console.WriteLine("Test: " + a + " "); tpPrice -= a; a = tpPrice * 0.5; a = (Math.Truncate(a / coef) * coef); tpPrice -= a; Console.WriteLine("Test: " + a + " "); a = (Math.Truncate(a / coef) * coef); tpPrice -= a; Console.WriteLine("Test: " + a + " "); Console.ReadLine(); } }

gives the following results:

Test: 0.010000000000000002

Test: 13.700000000000001

Test: 13.9

Test: 13.9

Please, note the last 2 digit in first result line and last 1 digit in the second result line

I consider it as major issue as it will affect calculation results and lead to wrong string representations of double values that will lead to stop working in communications with 3rd party services.

Configuration

.Net 5.0 Console app
Windows 10 Pro
x64
(seems fine in .Net Framework 4.8)

Regression?

seems fine in .Net Framework 4.8

Other information

N/A

Author: okarpov
Assignees: -
Labels:

area-System.Numerics, untriaged

Milestone: -

@okarpov
Copy link
Author

okarpov commented Oct 5, 2021

in .Net Core 5.0 (it seems that .Net Framework 4.8 has no such issue btw)

.NET Core 3 modified floating point parsing/ToString to be compliant with latest IEEE754 standard. .NET Framework was not standard complaint, and should be considered "wrong".

oh really? so you think we need an extra digit at the end because of just compliance with IEEE754?

original double has no/should not have that digit at the end and more over Math.Truncate supposes to truncate all decimals after dot doesn't?

@huoyaoyuan
Copy link
Member

oh really? so you think we need an extra digit at the end because of just compliance with IEEE754?

Yes. Compliance with IEEE754 matters, and the team decided it's more important than behavior match with .NET Framework.

original double has no/should not have that digit at the end

double is 2-based and it have infinite digits for 0.1.

@huoyaoyuan
Copy link
Member

Math.Truncate supposes to truncate all decimals after dot doesn't?

Adding/substracting truncated decimals together doesn't get truncated decimals of double because they are 2-based. You should consider to use decimal in some sort.

@okarpov
Copy link
Author

okarpov commented Oct 5, 2021

then you may close this issue.

If anyone struggles with unpredictet calculation results - be aware, this is extra digits that may be added to your double values because of IEEE754 compliance. Especially migrating from .Net Framwork to .Net Core

@huoyaoyuan
Copy link
Member

FYI: https://docs.microsoft.com/en-us/dotnet/core/compatibility/3.0#floating-point-formatting-and-parsing-behavior-changed

@okarpov
Copy link
Author

okarpov commented Oct 5, 2021

FYI: https://docs.microsoft.com/en-us/dotnet/core/compatibility/3.0#floating-point-formatting-and-parsing-behavior-changed

but the issue is not in Parsing or String Formating - the issue is inside of double itself.

storing double to SQL and selecting shows the same different extra digits at the end like 0.123000000001, 0.123000000002, 0.123000000004 etc

so it definitely affects all calculations

@huoyaoyuan
Copy link
Member

To inspect the representation of double, you can use BitConverter.DoubleToInt64Bits, which also exists in .NET Framework.
double is 2-based and it can never represent precised 0.123 etc.

@okarpov
Copy link
Author

okarpov commented Oct 5, 2021

To inspect the representation of double, you can use BitConverter.DoubleToInt64Bits, which also exists in .NET Framework. double is 2-based and it can never represent precised 0.123 etc.

You were right! My fault (i thought MS SQL shows real values without the formatting issues)

4623902032416630375 - .Net Core 5.0
4623902032416630375 - .Net Framework 4.8

@tannergooding
Copy link
Member

tannergooding commented Oct 5, 2021

Right. While the default parsing/formatting behavior changed to be IEEE 754 compliant, the actual underlying value represented in most cases did not. .NET Core simply ensures that the returned string is "roundtrippable" by default and that the original bitwise value can be successfully recovered (within the confines of the IEEE 754 specification; meaning everything but NaN).

For example, 0.01 is bitwise 0x3F847AE147AE147B. Likewise 0.1 * 0.1 is bitwise 0x3F847AE147AE147C.

  • This is similar to (0.1 + 0.2) != 0.3, which is a more well known form of the same

However, on .NET Framework both would have ToString() return "0.1", meaning that the following program results in:

var x = 0.1 * 0.1;
var y = double.Parse(x.ToString());
var equal = (x == y);
Console.WriteLine(x);
Console.WriteLine(x.ToString("G17"));
Console.WriteLine(BitConverter.DoubleToInt64Bits(x).ToString("X16"));
Console.WriteLine(y);
Console.WriteLine(y.ToString("G17"));
Console.WriteLine(BitConverter.DoubleToInt64Bits(y).ToString("X16"));
Console.WriteLine(equal);

.NET Framework

0.01
0.010000000000000002
3F847AE147AE147C
0.01
0.01
3F847AE147AE147B
False

.NET Core 3 or later

0.010000000000000002
0.010000000000000002
3F847AE147AE147C
0.010000000000000002
0.010000000000000002
3F847AE147AE147C
True

@tannergooding tannergooding added question Answer questions and provide assistance, not an issue with source code or documentation. and removed untriaged New issue has not been triaged by the area owner labels Oct 5, 2021
@tannergooding
Copy link
Member

Could you please close the issue if you have no further questions on the topic?

@jeffhandley jeffhandley added this to the Future milestone Oct 9, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Nov 17, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Numerics question Answer questions and provide assistance, not an issue with source code or documentation.
Projects
None yet
Development

No branches or pull requests

5 participants