Floating Point fixed length Number formatting c# - c#

I want to format a floating point number as follows in C# such that the entire width of the floating point number in C# is a fixed length (python equivalent format specifier 6.2f) I do NOT want it to be padded with 0's on the left but padded with a white space
100.00
90.45
7.23
0.00
what I have tried so far
string.Format({0:###.##},100);
string.Format({0:###.##},90.45);
string.Format({0:###.##},7.23);
string.Format({0:###.##},0.00);
but the output is incorrect
100
90.45
7.23
//nothing is printed
I have also gone through this but am unable to find a solution.
I am aware of the string.PadLeft method, but I am wondering if there is a more proper way than
(string.format({0,0.00},number)).PadLeft(6," ")
EDIT
I am specifically asking if there is a correct inbuilt method for the same, not if it can be done with same mathematical wizardry

If you always want 2 digits after the decimal, you can specify 00 in the format specifier. You would need to use a right aligned field width also (I used 6 as the max field width).
Try this:
void Main()
{
Console.WriteLine(string.Format("{0,6:##0.00}",100.0));
Console.WriteLine(string.Format("{0,6:##0.00}",90.45));
Console.WriteLine(string.Format("{0,6:##0.00}",7.23));
Console.WriteLine(string.Format("{0,6:##0.00}",0.00));
}
In LinqPad it outputs:
100.00
90.45
7.23
0.00

In .NET 5.0+ you don't need to call string.Format directly. Instead prefix your strings with $ and delimit variables inside {..}
Example
double myfloat1 = 100.0;
double myfloat2 = 0.2;
Debug.WriteLine($"{myfloat1,8:0.00}"); // 100.00
Debug.WriteLine($"{myfloat2,8:0.00}"); // 0.20
The code 8:0.00 formats as fixed 8-length (space-padded) with 2 decimal places.

Related

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.
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.

Why do the numeric format strings in C# round the number when not using decimals (F0)?

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

Format decimal to number of decimal places without scientific notation

I am currently formatting a double using the code:
myDouble.ToString("g4");
To get the first 4 decimal places. However I find this often switches over to scientific notation if the number is very large or very small. Is there an easy format string in C# to just have the first four decimal places, or zero if it is too small to be represented in that number of places?
For example, I would like:
1000 => 1000
0.1234567 => 0.1235
123456 => 123456 (Note: Not scientific notation)
0.000001234 => 0 (Note: Not scientific notation)
You can try like this:
0.1234567.ToString("0.####")
Also check Custom Numeric Format Strings
#
Replaces the "#" symbol with the corresponding digit if one is
present; otherwise, no digit appears in the result string.
Also as Jon as correctly pointed that it will round your number. See the note section
Rounding and Fixed-Point Format Strings
For fixed-point format strings
(that is, format strings that do not contain scientific notation
format characters), numbers are rounded to as many decimal places as
there are digit placeholders to the right of the decimal point.
Use the String.Format() method.
String.Format("{0:0.####}", 123.4567123); //output: 123.4567
Note: Num of #'s indicate the maximum number of digits after decimal that are required.
I agree with kjbartel comment.
I wanted exactly what the original question asked. But his question is slightly ambiguous.
The problem with ### format is it fills the slot if a digit can be represented or not.
So it does what the original question asks for some numbers but not others.
My basic need is, and it's a pretty common one, if the number is big I don't need to show decimal places. If the number is small I do want to show decimal places. Basically X number of significant digits.
The "Gn" Format will do significant digits, but it switches to scientific notation if you go over the number of digits. I don't want E notation, ever (same requirement as the question).
So I used fixed format ("Fn") but I calculate the width on the fly based on how "big" the number is.
var myFloatNumber = 123.4567;
var digits = (int) Math.Log10(myFloatNumber);
var maxDecimalplaces = 3;
var format = "F" + Math.Max(0,(maxDecimalplaces - digits));
I swear there was a way to do this in C++ (Visual Studio Flavor) in teh format statement or in C# and perhaps there is, but I can't find it.
So I came up with this. I could have converted to a string and measured length before decimal point as well. But converting it to a string twice felt wrong.

Strange Hex Number Encoding

I am working with text readable files which are exported from a client's systems that use a custom XML-like structure. I need to be able to parse and extract data from large numbers of these files with no documentation on how they are structured.
I have mostly worked out the file structure, however I am struggling with how values have been encoded. I can manually look up in the system the correct values as a comparison. Some examples:
Export Data = System Value
D411E848 = 500000
D40F86A = 100000
D41086A = 200000
I'm fairly sure the leading "D" is a token to say the field is a decimal or double value. The reason is that all numeric fields start with "D" and all text fields start with "S". The following "4" may also be part of the field data type, as all numeric fields seem to start with "D4".
However converting from Hex to Decimal on any combination of the export data value does not yield the correct result.
Any ideas how to do the conversion?
Extra data mappings:
Value Export File
1 D3FF
2 D4
3 D4008
4 D401
5 D4014
6 D4018
7 D401C
8 D402
9 D4022
10 D4024
100 D4059
1000 D408F4
100000 D40F86A
500000 D411E848
500001 D411E8484
500002 D411E8488
500003 D411E848C
500004 D411E849
500005 D411E8494
500006 D411E8498
500007 D411E849C
500008 D411E84A
500009 D411E84A4
500010 D411E84A8
Seems like a normal, but truncated, IEEE 754 64-bit (double precision) number.
0x408F400000000000 = 1000
408F4 (truncated)
D408F4 (prefixed with D)
0x411E848000000000 = 500000
411E848 (truncated)
D411E848 (prefixed with D)
Try converting it with the following website as a reference: http://www.binaryconvert.com/result_double.html?decimal=053048048048048048
I can see the pattern, starting from 2. Here are the steps to get decimal value from your custom format.
Skip D4 from the beginning of the string.
If LEN() < 3 fill with 0s to get at least 3 letters long string
Take 2 letters from the beginning of the string and convert using HEX to DEC converter
Add 1 to number get from point 3.
Get rest of the input string, skipping first 2 letters
Convert text from point 5. using HEX to DEC converter
Calculate POW(16, LEN(Y)), where Y is text from point 5.
Calculate X / Y, where X is number from point 6 and Y is text from point 7.
Calculate final result: POW(2, X)*(1 + Y), where X comes from point 4. and Y comes from point 9.
It may looks quite complicated, but it's actually quite simple.
I've created Excel Web App spreadsheat with results for all these steps for your sample inputs: http://sdrv.ms/1bO0wnz

Get the decimal part of a number and the number of places after the decimal point (C#)

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.

Categories