Formatting doubles for output in C#
The problem is that .NET will always round a double
to 15 significant decimal digits before applying your formatting, regardless of the precision requested by your format and regardless of the exact decimal value of the binary number.
I'd guess that the Visual Studio debugger has its own format/display routines that directly access the internal binary number, hence the discrepancies between your C# code, your C code and the debugger.
There's nothing built-in that will allow you to access the exact decimal value of a double
, or to enable you to format a double
to a specific number of decimal places, but you could do this yourself by picking apart the internal binary number and rebuilding it as a string representation of the decimal value.
Alternatively, you could use Jon Skeet's DoubleConverter
class (linked to from his "Binary floating point and .NET" article). This has a ToExactString
method which returns the exact decimal value of a double
. You could easily modify this to enable rounding of the output to a specific precision.
double i = 10 * 0.69;
Console.WriteLine(DoubleConverter.ToExactString(i));
Console.WriteLine(DoubleConverter.ToExactString(6.9 - i));
Console.WriteLine(DoubleConverter.ToExactString(6.9));
// 6.89999999999999946709294817992486059665679931640625
// 0.00000000000000088817841970012523233890533447265625
// 6.9000000000000003552713678800500929355621337890625
Formatting a double to two decimal places
string.Format
will not change the original value, but it will return a formatted string. For example:
Console.WriteLine("Earnings this week: {0:0.00}", answer);
Note: Console.WriteLine
allows inline string formatting. The above is equivalent to:
Console.WriteLine("Earnings this week: " + string.Format("{0:0.00}", answer));
How do I display a decimal value to 2 decimal places?
decimalVar.ToString("#.##"); // returns ".5" when decimalVar == 0.5m
or
decimalVar.ToString("0.##"); // returns "0.5" when decimalVar == 0.5m
or
decimalVar.ToString("0.00"); // returns "0.50" when decimalVar == 0.5m
C# double type formatting
Double values only hold 15 to 16 digits, you have 17 (if I counted right). Because 64 bit double numbers only hold 16 digits, your last digit is getting truncated and therefore when you convert the number to scientific notation, the last digit appears to have been truncated.
You should use Decimal instead. Decimal types can hold 128 bits of data, while double can only hold 64 bits.
Using String Format to show decimal up to 2 places or simple integer
An inelegant way would be:
var my = DoFormat(123.0);
With DoFormat
being something like:
public static string DoFormat( double myNumber )
{
var s = string.Format("{0:0.00}", myNumber);
if ( s.EndsWith("00") )
{
return ((int)myNumber).ToString();
}
else
{
return s;
}
}
Not elegant but working for me in similar situations in some projects.
double.ToString(string) with custom numeric format string does not produce the expected result
Floating-point numbers are approximations: they have a limited number of bits, and they do their best to represent a number close to the one you asked for within the constraints of the available bits.
Most of the time this works fine, but things start breaking down as you reach the limits of their precision which is around 16 digits for double
and 9 digits for float
.
Specifically, a double cannot represent 63712373026.615219
exactly. With G50
or Jon Skeet's DoubleConverter
, we can take a look at the exact number that the double does represent:
63712373026.615219.ToString("G50"); // 63712373026.6152191162109375
We're fine up to the 7th decimal place, but see how the closest representable number to 63712373026.615219
is actually a little bit larger?
With some trial-and-error, we can see the range of values which all get represented as 63712373026.6152191162109375
:
63712373026.6152230.ToString("G50"); // 63712373026.61522674560546875
63712373026.6152229.ToString("G50"); // 63712373026.6152191162109375
63712373026.615219.ToString("G50"); // 63712373026.6152191162109375
63712373026.6152154.ToString("G50"); // 63712373026.6152191162109375
63712373026.6152153.ToString("G50"); // 63712373026.61521148681640625
The precision limitations of double
mean that everything between 63712373026.6152154
and 63712373026.6152229
gets stored as the number 63712373026.6152191162109375
.
This presents a problem for the formatter: if you asked for 63712373026.615219.ToString("0.000000")
, should it give you 63712373026.615223
or 63712373026.615215
or anything in between?
In practice, what it appears to do is to work out the range of possible values which the double might be representing, and then round to the digits which are common to all. Since 63712373026.6152229
and 63712373026.6152154
and everything in between all start with 63712373026.6152
, that is what the formatter works with. Which is why it will print 63712373026.615200
if you force it to: it knows that it doesn't have enough information to fill in those last 2 digits.
Note that I think the round-trip and G17
formats are misleading you slightly. Round-trip basically prints the fewest digits which will be parsed back into the same underlying double value. So 63712373026.615219
contains the smallest number of decimal places which gets parsed back into 63712373026.6152191162109375
.
Note that they fixed R
on .NET 5:
63712373026.615219.ToString("R"); // 63712373026.61522
G17
just prints 17 digits, regardless of the underlying value of the double. Because double only has around 16 digits of precision, this is also enough to safely round-trip the double.
This can be seen with simpler values, such as 0.1
. double
, not being base 10, can't exactly represent 0.1
. Instead its closest value is:
0.1.ToString("G99"); // 0.1000000000000000055511151231257827021181583404541015625
However:
0.1.ToString("R"); // 0.1
The shortest value which gets represented as 0.1000000000000000055511151231257827021181583404541015625
is 0.1
, so this is what R
returns, even though it doesn't quite match the underlying representation. This is fine, because parsing 0.1
will result in a double whose underlying representation is 0.1000000000000000055511151231257827021181583404541015625
, thus successfully round-tripping it.
Format double in C#
Using format strings is explained in:
- Standard Numeric Format Strings
- Custom Numeric Format Strings
For example, try:
(0.56789).ToString("F2")
(0.56789).ToString("0.00")
.
Note that the resulting value is NOT truncated, but rounded in both cases, resulting in "0.57"
.
Format to two decimal places
Console.WriteLine("{0:N2}", ((double)n) / 1450);
Related Topics
C# Object Pooling Pattern Implementation
A Generic List of Anonymous Class
How to Drag and Drop Files into an Application
Displayname Attribute from Resources
What's the Difference Between Sortedlist and Sorteddictionary
Prevent .Net Garbage Collection for Short Period of Time
How to Move and Resize a Form Without a Border
How to Know If a Datetime Is Between a Daterange in C#
Calling a Method Every X Minutes
How to Read the Color of a Screen Pixel
C# Open a New Form Then Close the Current Form
How to Make an Event in the Usercontrol and Have It Handled in the Main Form
Wcf Named Pipe Minimal Example
Capture a Keyboard Keypress in the Background
How to Automatically Filter Out Soft Deleted Entities with Entity Framework