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

Conversions from BigInteger to Double are not always as precise as from Long #49611

Open
SingleAccretion opened this issue Mar 14, 2021 · 4 comments

Comments

@SingleAccretion
Copy link
Contributor

SingleAccretion commented Mar 14, 2021

Description

The gist of the issue is that some precision is apparently lost when using the explicit conversion operator operator double(BigInteger integer), as compared to a direct cast from long. Here's a very simple reproduction:

using System;
using System.Numerics;

BigInteger a = long.MaxValue / 2;

Console.WriteLine($"Source: {a}");
Console.WriteLine($"BigInt: {(double)a:F0}");
Console.WriteLine($"Long:   {(double)(long)a:F0}");

This will print:

Source: 4611686018427387903
BigInt: 4611686018427387392
Long:   4611686018427387904

Configuration

Tested with the latest bits from dotnet/installer - 6.0.100-preview.3.21164.5 on Windows 10 x64. I do not believe this is an architecture-specific issue as all the code that does it is fully managed and uses simple arithmetic.

Regression?

Not a regression - the reproduction works on net6.0, net5.0 and netcoreapp3.1. It appears that the code responsible for the conversion hasn't been touched in a long time.

@dotnet-issue-labeler dotnet-issue-labeler bot added area-System.Numerics untriaged New issue has not been triaged by the area owner labels Mar 14, 2021
@ghost
Copy link

ghost commented Mar 14, 2021

Tagging subscribers to this area: @tannergooding, @pgovind
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

The gist of the issue is that some precision is apparently lost when using the explicit conversion operator double(BigInteger integer), as compared to a direct cast from long. Here's a very simple reproduction:

 BigInteger a = long.MaxValue / 2;

 Console.WriteLine($"Source: {a}");
 Console.WriteLine($"BigInt: {(double)a:F0}");
 Console.WriteLine($"Long:   {(double)(long)a:F0}");

This will print:

Source: 4611686018427387903
BigInt: 4611686018427387392
Long:   4611686018427387904

Configuration

Tested with the latest bits from dotnet/installer - 6.0.100-preview.3.21164.5 on Windows 10 x64. I do not believe this is an architecture-specific issue as all the code that does it is fully managed and uses simple arithmetic.

Regression?

Not a regression - the reproduction works on net6.0, net5.0 and netcoreapp3.1. It appears that the code responsible for the conversion hasn't been touched in a long time.

Author: SingleAccretion
Assignees: -
Labels:

area-System.Numerics, untriaged

Milestone: -

@tannergooding tannergooding added bug and removed untriaged New issue has not been triaged by the area owner labels Jun 17, 2021
@tannergooding tannergooding added this to the 6.0.0 milestone Jun 17, 2021
@tannergooding
Copy link
Member

This looks like a rounding bug. Putting into 6.0 so we can do an investigation to determine if this is a simple fix or not.

@tannergooding tannergooding modified the milestones: 6.0.0, Future Jul 12, 2021
@Maximys
Copy link
Contributor

Maximys commented Jul 13, 2021

@tannergooding , currently I trying to fix this bug, but I have little question linked with RunSingleExplicitCastFromBigIntegerTests. Can you explain me this lines of test:

bigInteger = new BigInteger(float.MinValue);
bigInteger -= BigInteger.One;
VerifySingleExplicitCastFromBigInteger(float.MinValue, bigInteger);

This lines makes next:

  1. create BigInteger value by float.MinValue;
  2. decreasing this value;
  3. casting result value to float;
  4. checking equation of (float.MinValue - 1) and float.MinValue.

Is it normall? I think, it should return false but I'm not sure.
I see the same problem on next lines
Can you explain me?

@tannergooding
Copy link
Member

Is it normall? I think, it should return false but I'm not sure.

Yes. IEEE 754 binary floating-point numbers are non-exact and have a sliding epsilon (delta/difference) between values. This epsilon starts at float.Epsilon at 0 and doubles ever power of two. So, for example, the delta between 2^22 (4194304) and the next largest representable value is 0.5. That means 4194304 + any value that is less than 0.5 will result in the same value (4194304). This delta doubles at the next power of two (2^23, which is 8388608) and becomes 1.0, then again at the next (2^24, which is 16777216) to become 2.0, etc.

float.MaxValue is approx. 3.4028235E+38 which means the delta between it and the next largest number is very big (approx. 2.028241E+31) and anything smaller will result in float.MaxValue being returned.

Maximys added a commit to Maximys/runtime that referenced this issue Jul 16, 2021
Maximys added a commit to Maximys/runtime that referenced this issue Jul 16, 2021
Maximys added a commit to Maximys/runtime that referenced this issue Jul 16, 2021
Maximys added a commit to Maximys/runtime that referenced this issue Jul 16, 2021
Maximys added a commit to Maximys/runtime that referenced this issue Jul 30, 2021
Maximys added a commit to Maximys/runtime that referenced this issue Aug 2, 2021
Maximys added a commit to Maximys/runtime that referenced this issue Aug 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants