130

Is there a display formatter that will output decimals as these string representations in c# without doing any rounding?

// decimal -> string

20 -> 20
20.00 -> 20
20.5 -> 20.5
20.5000 -> 20.5
20.125 -> 20.125
20.12500 -> 20.125
0.000 -> 0

{0.#} will round, and using some Trim type function will not work with a bound numeric column in a grid.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
JC.
  • 11,561
  • 11
  • 41
  • 50

13 Answers13

172

Do you have a maximum number of decimal places you'll ever need to display? (Your examples have a max of 5).

If so, I would think that formatting with "0.#####" would do what you want.

    static void Main(string[] args)
    {
        var dList = new decimal[] { 20, 20.00m, 20.5m, 20.5000m, 20.125m, 20.12500m, 0.000m };

        foreach (var d in dList)
            Console.WriteLine(d.ToString("0.#####"));
    }
Patrick McDonald
  • 64,141
  • 14
  • 108
  • 120
Toby
  • 7,354
  • 3
  • 25
  • 26
  • 5
    And you can always throw an exorbitant* number of # symbols at the format. *not scientific – Anthony Pegram Jun 23 '10 at 20:43
  • 3
    +1 - incidentally, you don't need the leading #. "0.#####" works identically. And either way, this does round away from zero (just FYI). – TrueWill Jul 26 '10 at 22:44
  • 14
    @TrueWill Actually it does make a difference. d==0.1m then "0.#" renders "0.1" and "#.#" renders ".1" without the leading 0. Neither is wrong or right, just depends on what you want. – AaronLS Dec 20 '12 at 22:14
45

I just learned how to properly use the G format specifier. See the MSDN Documentation. There is a note a little way down that states that trailing zeros will be preserved for decimal types when no precision is specified. Why they would do this I do not know, but specifying the maximum number of digits for our precision should fix that problem. So for formatting decimals, G29 is the best bet.

decimal test = 20.5000m;
test.ToString("G"); // outputs 20.5000 like the documentation says it should
test.ToString("G29"); // outputs 20.5 which is exactly what we want
Schmalls
  • 1,434
  • 1
  • 19
  • 19
20

This string format should make your day: "0.#############################". Keep in mind that decimals can have at most 29 significant digits though.

Examples:

? (1000000.00000000000050000000000m).ToString("0.#############################")
-> 1000000.0000000000005

? (1000000.00000000000050000000001m).ToString("0.#############################")
-> 1000000.0000000000005

? (1000000.0000000000005000000001m).ToString("0.#############################")
-> 1000000.0000000000005000000001

? (9223372036854775807.0000000001m).ToString("0.#############################")
-> 9223372036854775807

? (9223372036854775807.000000001m).ToString("0.#############################")
-> 9223372036854775807.000000001
Erwin Mayer
  • 18,076
  • 9
  • 88
  • 126
  • 4
    This is the most general answer to the problem. Not sure why this isn't the default "G" format for decimal. The trailing zeros add no information. Microsoft documentation has a weird caveat for decimal: https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#GFormatString However, if the number is a Decimal and the precision specifier is omitted, fixed-point notation is always used and trailing zeros are preserved. – Eric Roller Oct 02 '17 at 23:20
11

This is yet another variation of what I saw above. In my case I need to preserve all significant digits to the right of the decimal point, meaning drop all zeros after the most significant digit. Just thought it would be nice to share. I cannot vouch for the efficiency of this though, but when try to achieve aesthetics, you are already pretty much damned to inefficiencies.

public static string ToTrimmedString(this decimal target)
{
    string strValue = target.ToString(); //Get the stock string

    //If there is a decimal point present
    if (strValue.Contains("."))
    {
        //Remove all trailing zeros
        strValue = strValue.TrimEnd('0');

        //If all we are left with is a decimal point
        if (strValue.EndsWith(".")) //then remove it
            strValue = strValue.TrimEnd('.');
    }

    return strValue;
}

That's all, just wanted to throw in my two cents.

dyslexicanaboko
  • 4,215
  • 2
  • 37
  • 43
6

Another solution, based on dyslexicanaboko's answer, but independent of the current culture:

public static string ToTrimmedString(this decimal num)
{
    string str = num.ToString();
    string decimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
    if (str.Contains(decimalSeparator))
    {
        str = str.TrimEnd('0');
        if(str.EndsWith(decimalSeparator))
        {
            str = str.RemoveFromEnd(1);
        }
    }
    return str;
}

public static string RemoveFromEnd(this string str, int characterCount)
{
    return str.Remove(str.Length - characterCount, characterCount);
}
Community
  • 1
  • 1
David Dostal
  • 711
  • 13
  • 17
  • Great. Except for SqlDecimal it is CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator despite regional settings. – user2091150 Oct 18 '19 at 09:06
3

You can use the G0 format string if you are happy to accept scientific notation as per the documentation:

Fixed-point notation is used if the exponent that would result from expressing the number in scientific notation is greater than -5 and less than the precision specifier; otherwise, scientific notation is used.

You can use this format string as a parameter to the .ToString() method, or by specifying it as a within an interpolated string. Both are shown below.

decimal hasTrailingZeros = 20.12500m;
Console.WriteLine(hasTrailingZeros.ToString("G0")); // outputs 20.125
Console.WriteLine($"{hasTrailingZeros:G0}"); // outputs 20.125

decimal fourDecimalPlaces = 0.0001m;
Console.WriteLine(fourDecimalPlaces.ToString("G0")); // outputs 0.0001
Console.WriteLine($"{fourDecimalPlaces:G0}"); // outputs 0.0001

decimal fiveDecimalPlaces = 0.00001m;
Console.WriteLine(fiveDecimalPlaces.ToString("G0")); // outputs 1E-05
Console.WriteLine($"{fiveDecimalPlaces:G0}"); // outputs 1E-05
Tim
  • 5,435
  • 7
  • 42
  • 62
2

Extension method:

public static class Extensions
{
    public static string TrimDouble(this string temp)
    {
        var value = temp.IndexOf('.') == -1 ? temp : temp.TrimEnd('.', '0');
        return value == string.Empty ? "0" : value;
    }
}

Example code:

double[] dvalues = {20, 20.00, 20.5, 20.5000, 20.125, 20.125000, 0.000};
foreach (var value in dvalues)
    Console.WriteLine(string.Format("{0} --> {1}", value, value.ToString().TrimDouble()));

Console.WriteLine("==================");

string[] svalues = {"20", "20.00", "20.5", "20.5000", "20.125", "20.125000", "0.000"};
foreach (var value in svalues)
    Console.WriteLine(string.Format("{0} --> {1}", value, value.TrimDouble()));

Output:

20 --> 20
20 --> 20
20,5 --> 20,5
20,5 --> 20,5
20,125 --> 20,125
20,125 --> 20,125
0 --> 0
==================
20 --> 20
20.00 --> 2
20.5 --> 20.5
20.5000 --> 20.5
20.125 --> 20.125
20.125000 --> 20.125
0.000 --> 0
jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • 1
    "20.00 --> 2" I think it is not good behaviour. To fix it call TrimEnd two times: TrimEnd('0'); TrimEnd(','); – Vadym Nov 27 '14 at 07:57
  • 4
    DO NOT USE the code written above. @VadymK is correct that its behavior is flawed. It will trim trailing zeros that are BEFORE the period if there is no period. – Sevin7 Jun 24 '16 at 13:36
2

I don't think it's possible out-of-the-box but a simple method like this should do it

public static string TrimDecimal(decimal value)
{
    string result = value.ToString(System.Globalization.CultureInfo.InvariantCulture);
    if (result.IndexOf('.') == -1)
        return result;

    return result.TrimEnd('0', '.');
}
Tim Skauge
  • 1,814
  • 2
  • 23
  • 35
2

It's quite easy to do out of the box:

Decimal YourValue; //just as example   
String YourString = YourValue.ToString().TrimEnd('0','.');

that will remove all trailing zeros from your Decimal.

The only thing that you need to do is add .ToString().TrimEnd('0','.'); to a decimal variable to convert a Decimal into a String without trailing zeros, like in the example above.

In some regions it should be a .ToString().TrimEnd('0',','); (where they use a comma instead of a point, but you can also add a dot and a comma as parameters to be sure).

(you can also add both as parameters)

nawfal
  • 70,104
  • 56
  • 326
  • 368
Mervin
  • 1,103
  • 4
  • 18
  • 26
  • gr8. Now you only have "0.0" left. Would procuce an empty string with your code. – jgauffin Jun 24 '10 at 06:07
  • 3
    Then I would suggest calling .ToString().TrimEnd('0').TrimEnd('.'). Also instead of '.' its better to have CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator so that it works across cultures. – Ε Г И І И О Nov 07 '11 at 08:05
  • @Mervin, please change your answer to reflect ΕГИІИО's comment. Your code would format `50.0m` as `5`, which is obviously an error. – Ethan Reesor Oct 17 '14 at 19:30
  • Downvoting. This will turn 20 into 2 :/ – nawfal Nov 17 '18 at 07:01
1
decimal val = 0.000000000100m;
string result = val == 0 ? "0" : val.ToString().TrimEnd('0').TrimEnd('.');
OKEEngine
  • 888
  • 11
  • 28
0

I ended up with the following code:

    public static string DropTrailingZeros(string test)
    {
        if (test.Contains(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator))
        {
            test = test.TrimEnd('0');
        }

        if (test.EndsWith(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator))
        {
            test = test.Substring(0,
                test.Length - CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator.Length);
        }

        return test;
    }
Arsen Zahray
  • 24,367
  • 48
  • 131
  • 224
0

I have end up with this variant:

public static string Decimal2StringCompact(decimal value, int maxDigits)
    {
        if (maxDigits < 0) maxDigits = 0;
        else if (maxDigits > 28) maxDigits = 28;
        return Math.Round(value, maxDigits, MidpointRounding.ToEven).ToString("0.############################", CultureInfo.InvariantCulture);
    }

Advantages:

you can specify the max number of significant digits after the point to display at runtime;

you can explicitly specify a round method;

you can explicitly control a culture.

lilo0
  • 895
  • 9
  • 12
0

You can create extension method

public static class ExtensionMethod {
  public static decimal simplify_decimal(this decimal value) => decimal.Parse($"{this:0.############}");
}
KevinBui
  • 880
  • 15
  • 27