dotnet / docs

This repository contains .NET Documentation.
https://learn.microsoft.com/dotnet
Creative Commons Attribution 4.0 International
4.22k stars 5.87k forks source link

Number formatting rounding discrepancy #22089

Open yac073 opened 3 years ago

yac073 commented 3 years ago

[Enter feedback here] Tested on .net core runtime 3.1.10

double num = 1234.5; var str = num.ToString("G4"); (1234, the rounding is using MidpointRounding.ToEven which is the expected behavior)

double money = 12.345; var moneyStr = money .ToString("C"); ($12.35, the rounding is using MidpointRounding.AwayFromZero, the result is expected to be $12.34 since formatting should be using MidpointRounding.ToEven)

decimal num2 = 1234.5m; var str2 = num2.ToString("G4"); (1235, the rounding is using MidpointRounding.AwayFromZero, the result is expected to be 1234 since formatting should be using MidpointRounding.ToEven)


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

adegeo commented 3 years ago

Hello. I can't seem to understand what the issue is. Can you give me a more detailed description of what you have and what you expected?

yac073 commented 3 years ago

In MS page Standard numeric format strings, it claims that


On .NET Framework and .NET Core up to .NET Core 2.0, the runtime selects the result with the greater least significant digit (that is, using MidpointRounding.AwayFromZero).
On .NET Core 2.1 and later, the runtime selects the result with an even least significant digit (that is, using MidpointRounding.ToEven).

However, from the example I showed above, some standard formatting failed to use MidpointRounding.ToEven and are still using MidpointRounding.AwayFromZero.

adegeo commented 3 years ago

@bartonjs @joperezr would either of you be able to answer this question?

ericstj commented 3 years ago

cc @tannergooding

tannergooding commented 3 years ago

For 1 and 2, this is by design and expected. For 3 I was not able to reproduce the issue.

For 1 and 2, floating-point literals may not produce exactly the value specified. Instead, the language parses the literal and produces the nearest representable value as per the IEEE 754 rules.

In the case of the first scenario, 1234.5 is exactly representable and so the 5 is treated as a proper midpoint value. However, in the second case 12.345 is not exactly representable and instead is internally 12.3450000000000006394884621840901672840118408203125, which is the nearest representable value to exact literal given. This means that the 5 is not actually a midpoint value because the underlying represented value is actually over 12.345 and so will always round up.

yac073 commented 3 years ago

Thanks for the clarification on 1 and 2. For 3 the screenshot of the sample program and csproj are these. image image

adegeo commented 3 years ago

@tannergooding can you check this out again when you get a chance? Thanks!

tannergooding commented 3 years ago

I'm able to repro now. Thanks!

I need to dig deeper to determine if this is a product or doc bug. There are a few places where decimal is handled differently and this might be "by design".

adegeo commented 3 years ago

Thank you for the update. I assigned the issue to you so that it's on your radar 😄

lwlwlwlw commented 2 years ago

Is there any update on this?

I am facing the same issue here, decimal is always rounded by MidpointRounding.AwayFromZero on .NET 6, which is contradict with the document (On .NET Core 2.1 and later, the runtime selects the result with an even least significant digit (that is, using MidpointRounding.ToEven).).

Just want to know if this is by design or a bug.

adegeo commented 2 years ago

@tannergooding ping

Would it be better to open this issue in the .NET product repo instead of the docs repo?

tannergooding commented 2 years ago

There are some known bugs with Round due to the underlying implementation (which has been around back into .NET Framework).

Scaling up, trying to round, and then scaling back down isn't "safe" and may introduce additional error that leads to incorrect rounding in various cases.

I won't be able to look more in depth at the additional feedback here until later.