'Can you explain this double to string conversion behavior in C#?
I'm trying to figure out why this happens and what is C# doing here.
Let's say we have a double: 277.3599853515625 (that's 13 digits after the period)
Then 277.3599853515625.ToString() -> "277.359985351563"
We lost a digit and it looks like the number got rounded UP.
But Math.Round(277.3599853515625,12) -> 277.359985351562 (looks like normal math rounding results in rounding DOWN)
I thought maybe if I give ToString() the formatting I want it would do the correct thing (give me the entire number):
277.3599853515625.ToString("0.#############") -> "277.359985351563" (that's 13 # signs, and still lost a digit and rounded UP)
If I reduce the last digit from 5 to 4, it rounds DOWN:
277.3599853515624.ToString("0.#############") -> "277.359985351562"
So it is clearly doing the rounding, but the rounding rules are different from normal math rounding. My first thought was that it's just treating 5 different, when normal rounding rounds 5 down, the ToString rounds it up, but look at this:
277.3599853515624999.ToString("0.#############") -> "277.359985351563" (WHAT?!?!?!?)
Do you have any idea what is happening here and what exactly C#'s logic in ToString() does?
The reason I'm asking is that I need to understand how to replicate the same behavior in a different language.
Thank you.
Solution 1:[1]
-
277.3599853515625.ToString() -> "277.359985351563"or
277.3599853515624.ToString("0.#############") -> "277.359985351562"In this case, the
ToStringmethod is usingMidpointRounding.AwayFromZeroso that is why it converts 2 to 3 when the last digit is 5. For Reference, use this link: https://docs.microsoft.com/en-us/dotnet/api/system.midpointrounding?view=net-6.0#system-midpointrounding-awayfromzero -
Math.Round(277.3599853515625,12) -> 277.359985351562**In this case,
Math.RoundusesMidpointRounding.ToEvenby default and rounds midpoint values to the nearest even number. Need to explicitly define specificMidpointRoundingifToEvenis not required. For Reference, use this link: https://docs.microsoft.com/en-us/dotnet/api/system.math.round?view=net-6.0 -
277.3599853515624999.ToString("0.#############") -> "277.359985351563"(WHAT?!?!?!?)
Here, there are two concepts. One is that
ToStringconsiders this 277.3599853515624999 asDoubletype, so it is a 16 digit number; that is why you are getting 16 digits.Console.WriteLine(277.3599853515624999.GetType()); // System.Double;Double-15-16 digits (64 bit)
Decimal -28-29 significant digits (128 bit)Thus, if change this (277.3599853515624999) to (277.3599853515624999m.ToString()),
then you get 277.3599853515624999And the second one is that there is also rounding done by
MidpointRounding.AwayFromZero.
You can play with the below code:
Decimal h1= 277.3599853515624999m;
string hh= "277.3599853515624999";
string h = 277.3599853515624999m.ToString();
Console.WriteLine(277.3599853515624999.GetType()); // System.Double;
Console.WriteLine(h);
string hhh = Math.Round(277.345,2,MidpointRounding.AwayFromZero).ToString();
Console.WriteLine(hhh);
I hope now there is a clear picture.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Jeremy Caney |
