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

double representation of decimal.MaxValue cannot be converted properly to decimal #68042

Closed
christothes opened this issue Apr 14, 2022 · 5 comments
Assignees
Milestone

Comments

@christothes
Copy link

Description

decimal.MaxValue (79228162514264337593543950335) is representable within the range of double (as 7.9228162514264338E+28), however trying to cast a value back to decimal it throws OverflowException "Value was either too large or too small for a Decimal."

Reproduction Steps

double d = Decimal.ToDouble(decimal.MaxValue);
decimal converted = (decimal)(d);

Expected behavior

The operation succeeds and converted == 7.9228162514264338E+28

Actual behavior

throws OverflowException "Value was either too large or too small for a Decimal."

Regression?

No response

Known Workarounds

No response

Configuration

reproduces in net6.0 and net5.0. I haven't tried others.

Other information

No response

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

ghost commented Apr 14, 2022

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

Issue Details

Description

decimal.MaxValue (79228162514264337593543950335) is representable within the range of double (as 7.9228162514264338E+28), however trying to cast a value back to decimal it throws OverflowException "Value was either too large or too small for a Decimal."

Reproduction Steps

double d = Decimal.ToDouble(decimal.MaxValue);
decimal converted = (decimal)(d);

Expected behavior

The operation succeeds and converted == 7.9228162514264338E+28

Actual behavior

throws OverflowException "Value was either too large or too small for a Decimal."

Regression?

No response

Known Workarounds

No response

Configuration

reproduces in net6.0 and net5.0. I haven't tried others.

Other information

No response

Author: christothes
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 Apr 14, 2022
@Clockwork-Muse
Copy link
Contributor

decimal.MaxValue (79228162514264337593543950335) is representable within the range of double

This is kinda false, this exact value is not representable. The nearest round-trip value appears to be 7.922816251426434E+28, which is greater (not lower) than the maximum value.

Doubles can no longer represent all integers over 2 ^ 53, or 9007199254740992 (16 digits).

@Mohit-Chakraborty
Copy link

I did a bit of digging and it looks like it follows from the rules of explicit conversions between different numeric types.

Specifically, these two rules -

When you convert decimal to float or double, the source value is rounded to the nearest float or double value, respectively.

and

When you convert float or double to decimal, the source value is converted to decimal representation and rounded to the nearest number after the 28th decimal place if required. If the source value is NaN (not a number), infinity, or too large to be represented as a decimal, an OverflowException is thrown.

In our case, we go from -
decimal -> 79228162514264337593543950335
to double to
decimal -> 79228162514264340000000000000
which is now larger than decimal.MaxValue.

@tannergooding
Copy link
Member

Note that its actually:
decimal: 79228162514264337593543950335 to double: 79228162514264337593543950336. 7.9228162514264338E+28 is just the shortest representable string for that double and the trailing digits are not actually zero.

This is still "too large" and should throw as specified. -- I had misread the conversion direction at first thinking the conversion to double was throwing, when it shouldn't.

There is still a separate issue where precision is unnecessarily lost for other values:

double x = Math.BitDecrement(79228162514264337593543950335.0);
decimal y = (decimal)x;

Console.WriteLine(x.ToString("G99"));    // 79228162514264328797450928128
Console.WriteLine(y.ToString("G99"));    // 79228162514264300000000000000

@dakersnar dakersnar self-assigned this Apr 26, 2022
@jeffhandley jeffhandley added this to the 7.0.0 milestone Apr 27, 2022
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jun 11, 2022
@dakersnar
Copy link
Contributor

Closing this and moving to the more in-depth issue above

@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jul 13, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Aug 13, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants