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.
277.3599853515625.ToString() -> "277.359985351563"
or
277.3599853515624.ToString("0.#############") -> "277.359985351562"
In this case, the ToString method is using MidpointRounding.AwayFromZero so that is why it converts 2 to 3 when the last digit is 5.
For Reference, use this link: https://learn.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.Round uses MidpointRounding.ToEven by default and rounds midpoint values to the nearest even number. Need to explicitly define specific MidpointRounding if ToEven is not required.
For Reference, use this link: https://learn.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 ToString considers this
277.3599853515624999 as Double type, 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.3599853515624999
And 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.
Related
I have encountered something very weird when it comes to the standard numeric format strings in C#. This is probably a known quirk but i can't find anything documenting it and i can't figure out a way to actually get what i want.
I want to take a number like 17.929333333333489 and format it with no decimal places. So i just want "17". But when run this code.
decimal what = 17.929333333333489m;
Console.WriteLine(what.ToString("F0"));
I get this output
18
Looking at Microsoft's documentation on it their examples show the same output.
https://msdn.microsoft.com/en-us/library/kfsatb94(v=vs.110).aspx
// F: -195489100.84
// F0: -195489101
// F1: -195489100.8
// F2: -195489100.84
// F3: -195489100.838
Here is a code example showing the odd issue.
http://csharppad.com/gist/d67ddf5d0535c4fe8e39
This issue is not only limited to the standard ones like "F" and "N" but also the custom ones like "#".
How can i use the standard "F0" formatter and not have it round my number?
From the documentation on Standard Numeric Format Strings:
xx is an optional integer called the precision specifier. The precision specifier ranges from 0 to 99 and affects the number of digits in the result. Note that the precision specifier controls the number of digits in the string representation of a number. It does not round the number itself. To perform a rounding operation, use the Math.Ceiling, Math.Floor, or Math.Round method.
When precision specifier controls the number of fractional digits in the result string, the result strings reflect numbers that are rounded away from zero (that is, using MidpointRounding.AwayFromZero).
So the documentation does indeed discuss this, and there is no apparent way to prevent rounding of the output purely through a format string.
The best I can offer is to truncate the number yourself using Math.Truncate():
decimal what = 17.929333333333489m;
decimal truncatedWhat = Math.Truncate(what);
Console.WriteLine(truncatedWhat.ToString("F0"));
I believe using decimal with "m" at the end rounds up at the given decimal place.
Here is what I experimented.
decimal what = 17.429333333333489m;
Console.WriteLine(what.ToString("F0"));
Console.WriteLine(what.ToString("N0"));
Console.WriteLine(what.ToString("F1"));
Console.WriteLine(what.ToString("N1"))
17
17
17.4
17.4
If you want to get 17, I used different approach using int and deciam
double what = 17.929333333333489;
Console.WriteLine(string.Format("{0:0}", (int)what));
Console.WriteLine(string.Format("{0:0}", what));
Console.WriteLine(string.Format("{0:0.00}", Math.Floor(what*100)/100));
Console.WriteLine(string.Format("{0:0.00}", what));
17
18
17.92
17.93
This question already has answers here:
How do I display a decimal value to 2 decimal places?
(19 answers)
Closed 8 years ago.
UPDATE
It's so simple...
When I try to convert the value $ 1.50 from a textbox to a decimal variable, like this:
decimal value = Convert.ToDecimal(textbox1.text.SubString(1));
OR
decimal value = Decimal.Parse(textbox1.text.SubString(1));
I get this result: 1.5.
I know that 1.5 and 1,50 worth the same. But I want to know if it's possible to have two digits after the dot on a decimal variable.
I want to have this as result: 1.50 instead of 1.5 even if these two values worth the same...
I want to have this as result: 1.50 instead of 1.5 even if these two values worth the same..
You have 1.50 or 1.500 or 1.5000. all depending on how you decide to format it / print it.
Your decimal value is stored in floating point format. How many decimal points you see is about output, not storage (at least until you reach the limit of the precision of the particular binary format, and 2 decimal places is nowhere close). A C# Decimal stores up to 29 significant digits.
See this reference. It gives an example of a currency format. It prints something like:
My amount = $1.50
But, you aren't storing a $ sign..., so where does it come from? The same place the "1.50" comes from, it is in your format specifier.
http://msdn.microsoft.com/en-us/library/364x0z75.aspx
Console.WriteLine("My amount = {0:C}", x);
var s = String.Format("My amount = {0:C}", x);
It is no different than saying, how do I store 1/3 (a repeating decimal)?
Well, it isn't 0.33, but if I only look at the first 2 digits, then it is 0.33. The closer i look (the more decimal places I ask for in the format), the more I get.
0.33333333333333... but that doesn't equal 0.330
You're confusing storage of the numeric value with rendering it as a string (display).
decimal a=1.5;
decimal b=1.50;
decimal c=1.500;
In memory: the zeros are kept to keep track of how much precision is desired. See the link in the comment by Chris Dunaway below.
However, note these tests:
(a==b) = true
(b==c)=true
Parsing ignores the trailing zeros, so your example one creates them, then they're ignored, as they're mathmatically irrelevant.
Now how you convert to string is a different story:
a.ToString("N4") returns the string "1.5000" (b. and c. the same)
a.ToString("N2") returns the string "1.50"
As the link in the comment explains, if you just to a.ToString, trailing zeros are retained.
If you store it in a database column as type 'decimal', it might be a different story - I haven't researched the results. These are the rules that .Net uses and while the databases might use different rules, these behaviours often follow official standards, so if you do your research you might find that the database behaves the same way!
The important thing to remember is that there is a difference between the way numbers are stored in memory and the way they are represented as strings. Floating point numbers may not retain trailing zeros this way, it's up to the rules of the in-memory storage of the type (usually set by standards bodies in very specific, detailed ways).
I want to implement logic of rounding up to 3 decimal positions after rounding.
If the value of decimal place 1 – 3 is equal to 000 (regardless of the whole number), and the value of the 4th thru the 10th decimal place is greater than 0, the 3rd decimal place in the display will round up.
example:
1.1230000000 --> 1.123
1.1230010000 --> 1.123
1.1230600000 --> 1.124
1.0000010000 --> 1.001
1.0003000000 --> 1.003
5.0000001234 --> 5.001
looking forward
Math.Ceiling(myNumber*1000)/1000;
should do the trick for positive numbers. Test if you get the desired behavior for negative numbers (not given in your examples). If not, use Floor instead of Ceiling for negatives.
I am not a sharepoint person, but I have general languages experience.
not Math.ceil() use js Int() or whatever int() function you have.
ceil() regardless of frac value != 0 always moves up to next integer. parseInt chops off the frac part, which is what he was really trying to do. I can't find any sharepoint API references on msdn.
I am basing this on the JavaScript/ecmascript 262 5.1 spec.
if you want number of digits like 5 digits, it's
Function truncateDigits(Double n, Integer numdigits))
return Int(Math.Power(10,numdigits)*n)/Math.Power(10,numdigits);
EndFunction
but be aware that floating point has a problem with even repeatedly adding or subtracting 0.1 and you get wild results due to floating point error. to work with financials, you should use a decimal data type if there is one, the decimal data type if the language provides it does not have this problem. otherwise, you can simulate fixed-point fractional by looking at a large integer type and faking a decimal point in the middle or where you want it and calculating accordingly. display would require that you create your own display routines. but it would be fast and limited in abilities (no trig funcs, powers you would have to write on your own based on integers). you would need to come up with your own arithmetic routines, because normal ones assume the decimal point in the wrong place.
Does anyone know of an elegant way to get the decimal part of a number only? In particular I am looking to get the exact number of places after the decimal point so that the number can be formatted appropriately. I was wondering if there is away to do this without any kind of string extraction using the culture specific decimal separator....
For example
98.0 would be formatted as 98
98.20 would be formatted as 98.2
98.2765 would be formatted as 98.2765 etc.
It it's only for formatting purposes, just calling ToString will do the trick, I guess?
double d = (double)5 / 4;
Console.WriteLine(d.ToString()); // prints 1.75
d = (double)7 / 2;
Console.WriteLine(d.ToString()); // prints 3.5
d = 7;
Console.WriteLine(d.ToString()); // prints 7
That will, of course, format the number according to the current culture (meaning that the decimal sign, thousand separators and such will vary).
Update
As Clement H points out in the comments; if we are dealing with great numbers, at some point d.ToString() will return a string with scientific formatting instead (such as "1E+16" instead of "10000000000000000"). One way to overcome this probem, and force the full number to be printed, is to use d.ToString("0.#"), which will also result in the same output for lower numbers as the code sample above produces.
You can get all of the relevant information from the Decimal.GetBits method assuming you really mean System.Decimal. (If you're talking about decimal formatting of a float/double, please clarify the question.)
Basically GetBits will return you 4 integers in an array.
You can use the scaling factor (the fourth integer, after masking out the sign) to indicate the number of decimal places, but you should be aware that it's not necessarily the number of significant decimal places. In particular, the decimal representations of 1 and 1.0 are different (the former is 1/1, the latter is 10/10).
Unfortunately, manipulating the 96 bit integer is going to require some fiddly arithmetic unless you can use .NET 4.0 and BigInteger.
To be honest, you'll get a simpler solution by using the built in formatting with CultureInfo.InvariantCulture and then finding everything to the right of "."
Just to expand on the point about getbits, this expression gets the scaling factor from a decimal called foo:
(decimal.GetBits(foo)[3] & 16711680)>>16
You could use the Int() function to get the whole number component, then subtract from the original.
I have the following test code:
decimal test1 = 0.0500000000000000045656554454M;
double test2 = (double)test1;
This results in test2 showing as 0.05 when debugging. Why is it being rounded to 2 decimal places?
Thanks
The value from that conversion is actually 0.050000000000000009714451465470119728706777095794677734375, as shown by DoubleConverter. That's the exact value of the nearest double to the decimal you converted.
When you use the debugger or normal string formatting, you aren't usually shown the exact result.
The reason is that double can contain no more than 15-16 significant digits.
see double (C# Reference)
You should take a look at this article about floating-point arithmetic and .NET. The rounding occurs due to a combination of how the number gets converted to a double-precision floating point value and how it is formatted when printed, since .NET defaults to 15 decimals for doubles, and your original number contains decimal past the 15th.
You could try test2.ToString("0.000000000000000000000000") to see if you might squeeze out any more information from the number, but I doubt it will.
There are two reasons I can think of:
Due to the different representation of decimal and double. See this article for more information about floating point representation. It is possible that there are not enough bits for the whole number representation in the double.
Due to the way numbers are printed. It is possible that in your printing options, there are less than 18 numbers after the decimal point specified - in which case, you'll get the rounded result.
I would check for tweaking the printing options first to make sure that the problem isn't there first.
.. But know that the only solution for the first problem is stop using double :-)