239

I have some fields returned by a collection as

2.4200
2.0044
2.0000

I want results like

2.42
2.0044
2

I tried with String.Format, but it returns 2.0000 and setting it to N0 rounds the other values as well.

user247702
  • 23,641
  • 15
  • 110
  • 157
Zo Has
  • 12,599
  • 22
  • 87
  • 149
  • 3
    initially record type is string?? – Singleton Dec 24 '10 at 10:56
  • 2
    See my answer: `String.Format()` with 'G' should get what you want.. i've updated my answer with a link to standard numeric formats. Very userful. – Dog Ears Dec 24 '10 at 11:15
  • 3
    A decimal can be casted to `double`, and the default `ToString` for double emits the trailing zeros. read [this](http://stackoverflow.com/questions/4525854/remove-trailing-zeroes/4525981#4525981) – Shimmy Weitzhandler Dec 24 '10 at 11:30
  • 2
    And it will probably cost less performance (interms of very large amount of records) than passing the "G" as a string-format to the `ToString` function. – Shimmy Weitzhandler Dec 25 '10 at 16:30
  • Another similar question: http://stackoverflow.com/questions/2109494 – Dog Ears Aug 04 '11 at 08:23
  • possible duplicate of [Remove 0s from the end of a decimal value](http://stackoverflow.com/questions/2109494/remove-0s-from-the-end-of-a-decimal-value) – bluish Oct 22 '13 at 12:07
  • 6
    You shouldn't convert a decimal to a double, it will lose significance, and may introduce power of two rounding inaccuracies. – kristianp Feb 19 '15 at 05:52
  • 2
    string str = numberValue.ToString("0.##############"); – Mike Gledhill Jun 10 '16 at 15:07

23 Answers23

290

I ran into the same problem but in a case where I do not have control of the output to string, which was taken care of by a library. After looking into details in the implementation of the Decimal type (see http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx), I came up with a neat trick (here as an extension method):

public static decimal Normalize(this decimal value)
{
    return value/1.000000000000000000000000000000000m;
}

The exponent part of the decimal is reduced to just what is needed. Calling ToString() on the output decimal will write the number without any trailing 0. E.g.

1.200m.Normalize().ToString();
Thomas Materna
  • 3,619
  • 1
  • 17
  • 14
  • 42
    This answer owns because unlike basically every other answer on this question (and even the whole subject) is actually WORKS and does what the OP is asking. – Coxy Dec 13 '11 at 01:33
  • 3
    is the number of 0s an exact quantity here or just to cover most expected values? – Simon_Weaver Sep 23 '12 at 05:48
  • 5
    MSDN states "The scaling factor is implicitly the number 10, raised to an exponent ranging from 0 to 28", which I understand as the decimal number will have at most 28 digits past the decimal point. Any number of zeros >= 28 should work. – Thomas Materna Oct 09 '12 at 16:32
  • 3
    This is the best answer! The number in the answer has 34 figures, the `1` followed by 33 `0`-s, but that creates exactly the same `decimal` instance as a number with 29 figures, one `1` and 28 `0`-s. Just like @ThomasMaterna said in his comment. No `System.Decimal` can have more than 29 figures (numbers with leading digits greater than `79228...` can have only *28* figures in total). If you want ***more*** trailing zeroes, multiply by `1.000...m` instead of dividing. Also, `Math.Round` can chop off some zeroes, for example `Math.Round(27.40000m, 2)` gives `27.40m`, so only one zero left. – Jeppe Stig Nielsen Aug 26 '13 at 12:05
  • 2
    Great trick but does NOT work on Mono and is not guarantueed to work on feature versions of .NET – Bigjim Dec 17 '13 at 23:25
  • 1
    @StefanSteinegger: Just tried it in LinqPad. It doesn't work (Thomas's trick works fine). `decimal x = 5.0M;decimal y = 1M;(x/y).Dump();` – Brian Jan 20 '15 at 18:49
  • @ThomasMaterna: sorry, I was using linqpad for the first time and I confused a compilation error for a runtime error... deleting my previous comment ;) – digEmAll Dec 01 '16 at 16:31
  • Exactly. `0.1m == 0.10m` but `0.1m.ToString() != 0.10m.ToString()`. Equal, but different. – Jesper Mygind Oct 08 '20 at 12:36
  • I ran into this when upgrading our Npgsql (ADO driver for Postgres). Somewhere between 3.x and 5.x they decided to preserve numeric precision as stored on tables in decimal-values read from the database. – Rune Jun 21 '21 at 12:56
  • Great answer, but the problem with using this in vb.net is it sometimes reformats the line and strips all the 0's out so it ends up with just 1@, which doesn't work – Brain2000 Jan 15 '22 at 23:00
205

Is it not as simple as this, if the input IS a string? You can use one of these:

string.Format("{0:G29}", decimal.Parse("2.0044"))

decimal.Parse("2.0044").ToString("G29")

2.0m.ToString("G29")

This should work for all input.

Update Check out the Standard Numeric Formats I've had to explicitly set the precision specifier to 29 as the docs clearly state:

However, if the number is a Decimal and the precision specifier is omitted, fixed-point notation is always used and trailing zeros are preserved

Update Konrad pointed out in the comments:

Watch out for values like 0.000001. G29 format will present them in the shortest possible way so it will switch to the exponential notation. string.Format("{0:G29}", decimal.Parse("0.00000001",System.Globalization.CultureInfo.GetCultureInfo("en-US"))) will give "1E-08" as the result.

Community
  • 1
  • 1
Dog Ears
  • 9,637
  • 5
  • 37
  • 54
  • 16
    Watch out for values like 0.000001. G29 format will present them in the shortest possible way so it will switch to the exponential notation. string.Format("{0:G29}", decimal.Parse("0.00000001",System.Globalization.CultureInfo.GetCultureInfo("en-US"))) will give "1E-08" as the result – Konrad Aug 31 '11 at 14:51
  • 3
    Note that `"G0"` works just like `"G29"`, since, as stated, when the precision specifier is zero, the default precision for the type (that is 29 for `decimal`) is used. – Jeppe Stig Nielsen Jul 07 '13 at 22:08
  • 16
    @Konrad - is there a way to avoid the Scientific notation for numbers that have 5 or 6 decimal places? – Jill Oct 24 '13 at 14:23
  • @DogEars what about if I get 20,00000 from database and I want to present on my UI only 20,00? – Roxy'Pro Feb 02 '17 at 13:34
  • Warning! This answer works only if your current culture expects a point as decimal separator. In many cultures the point is assumed to be the thousands separator and the result will be totally wrong. Always specify the culture info in these conversions. – Steve Jun 29 '18 at 19:02
  • This answer is incomplete! Doesn’t work for 0.00001 as it reverts to scientific notation! What is the answer in 2021????? – Luke Jun 17 '21 at 23:23
  • decimal d = 2.3; didnt work, it gave .ToString("G29") as 2.999999999998 – Pratyush Dhanuka Jul 14 '22 at 10:49
109

In my opinion its safer to use Custom Numeric Format Strings.

decimal d = 0.00000000000010000000000m;
string custom = d.ToString("0.#########################");
// gives: 0,0000000000001
string general = d.ToString("G29");
// gives: 1E-13
Michał Powaga
  • 22,561
  • 8
  • 51
  • 62
41

I use this code to avoid "G29" scientific notation:

public static string DecimalToString(this decimal dec)
{
    string strdec = dec.ToString(CultureInfo.InvariantCulture);
    return strdec.Contains(".") ? strdec.TrimEnd('0').TrimEnd('.') : strdec;
}

EDIT: using system CultureInfo.NumberFormat.NumberDecimalSeparator :

public static string DecimalToString(this decimal dec)
{
    string sep = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
    string strdec = dec.ToString(CultureInfo.CurrentCulture);
    return strdec.Contains(sep) ? strdec.TrimEnd('0').TrimEnd(sep.ToCharArray()) : strdec;
}
x7BiT
  • 447
  • 4
  • 5
  • 10
    Nice, but it won't work for cultures that use comma for the decimal separator. You'd need to use `Culture.NumberFormat.NumberDecimalSeparator` – blearyeye Jan 12 '17 at 13:57
  • 1
    Best approach. Everything else is needlessly complicated. Just remember always to check if your number contains decimal point. – RRM Oct 30 '18 at 15:33
  • 1
    To be an extension is missing the "this" before the parameter: public static string DecimalToString(this decimal dec) – João Antunes Jan 23 '20 at 16:15
  • Thank you very much for the comments. I have updated the answer with a new version with the tips. – x7BiT Feb 29 '20 at 21:44
29

Use the hash (#) symbol to only display trailing 0's when necessary. See the tests below.

decimal num1 = 13.1534545765;
decimal num2 = 49.100145;
decimal num3 = 30.000235;

num1.ToString("0.##");       //13.15%
num2.ToString("0.##");       //49.1%
num3.ToString("0.##");       //30%
Fizzix
  • 23,679
  • 38
  • 110
  • 176
  • 10
    Not an answer. You are not removing trailing zeros, you are removing precision. Your proposed method `num1.ToString("0.##")` should return `13.1534545765` (no change) becouse there aren't any trailing zeros. – Jan 'splite' K. Jan 10 '17 at 17:23
  • 2
    And that's exactly why I'm displaying the answers to my three proposed inputs so users can see how my solution works. – Fizzix Jun 19 '17 at 04:17
  • 1
    It does not answer the question even if you list proposed inputs. – Dave Friedel Dec 24 '17 at 03:56
  • @Jan'splite'K. It's the same, because trailing zeros often preserve significance, therefore the precision decreases when one removes them. Just google the topic. – Crouching Kitten Jan 07 '20 at 16:48
  • @CrouchingKitten Well, literally speaking you are right. But still not an answer, see examle output. – Jan 'splite' K. Jan 09 '20 at 14:05
  • 1
    Perfect answer to the question. BEST answer in my opinion. Comment that it removes precision is correct, but that was not the quesiton. Most important -- it's what I need. My users will typically enter whole numbers, maybe something like 2.5, but I need to support 3 digits beyond the decimal point. So I want to give them what they entered, not with a bunch of zeroes they didn't enter. e.g., Console.Write ($"num3={num3:0.##}"); => num3=30 – bobwki Mar 19 '20 at 00:54
  • Bobwki,, Michael Powaga had this answer 3 years before, sans rounding examples. – Gerard ONeill Mar 08 '22 at 03:12
  • Jan, any answer will remove significant digits if it removes 0s. This answer can display all of those digits by expanding the amount of #'s. Choices are always made, including the choice to store something in a double as opposed to a decimal. – Gerard ONeill Mar 08 '22 at 03:16
  • @Bobwki This does NOT give back what the user entered if they deliberately entered a trailing zero. 0 is as valid as value as 1 - 9. Using a non-invertible operation, e.g. one that removes precision, cannot give you what you want. – Paul Childs Jun 20 '22 at 00:01
20

I found an elegant solution from http://dobrzanski.net/2009/05/14/c-decimaltostring-and-how-to-get-rid-of-trailing-zeros/

Basically

decimal v=2.4200M;

v.ToString("#.######"); // Will return 2.42. The number of # is how many decimal digits you support.
TotPeRo
  • 6,561
  • 4
  • 47
  • 60
DHornpout
  • 6,400
  • 4
  • 25
  • 20
4

A very low level approach, but I belive this would be the most performant way by only using fast integer calculations (and no slow string parsing and culture sensitive methods):

public static decimal Normalize(this decimal d)
{
    int[] bits = decimal.GetBits(d);

    int sign = bits[3] & (1 << 31);
    int exp = (bits[3] >> 16) & 0x1f;

    uint a = (uint)bits[2]; // Top bits
    uint b = (uint)bits[1]; // Middle bits
    uint c = (uint)bits[0]; // Bottom bits

    while (exp > 0 && ((a % 5) * 6 + (b % 5) * 6 + c) % 10 == 0)
    {
        uint r;
        a = DivideBy10((uint)0, a, out r);
        b = DivideBy10(r, b, out r);
        c = DivideBy10(r, c, out r);
        exp--;
    }

    bits[0] = (int)c;
    bits[1] = (int)b;
    bits[2] = (int)a;
    bits[3] = (exp << 16) | sign;
    return new decimal(bits);
}

private static uint DivideBy10(uint highBits, uint lowBits, out uint remainder)
{
    ulong total = highBits;
    total <<= 32;
    total = total | (ulong)lowBits;

    remainder = (uint)(total % 10L);
    return (uint)(total / 10L);
}
Bigjim
  • 2,145
  • 19
  • 19
  • 2
    I had tried to go this route, but optimized it further so it got really much faster than yours, by e.g. not dividing the *hi* and *mid* ints (your `a` and `b`) in every while iteration if they are all zero anyway. However then I found [this surprisingly simple solution that also easily beats what you and I have come up with performance wise](http://stackoverflow.com/a/7983397/709537). – Evgeniy Berezovsky Apr 08 '15 at 03:13
4

This is simple.

decimal decNumber = Convert.ToDecimal(value);
        return decNumber.ToString("0.####");

Tested.

Cheers :)

Rakesh
  • 97
  • 8
1

Depends on what your number represents and how you want to manage the values: is it a currency, do you need rounding or truncation, do you need this rounding only for display?

If for display consider formatting the numbers are x.ToString("")

http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx and

http://msdn.microsoft.com/en-us/library/0c899ak8.aspx

If it is just rounding, use Math.Round overload that requires a MidPointRounding overload

http://msdn.microsoft.com/en-us/library/ms131274.aspx)

If you get your value from a database consider casting instead of conversion: double value = (decimal)myRecord["columnName"];

florin
  • 405
  • 4
  • 20
1

This will work:

decimal source = 2.4200m;
string output = ((double)source).ToString();

Or if your initial value is string:

string source = "2.4200";
string output = double.Parse(source).ToString();

Pay attention to this comment.

Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
  • Thanks for the reply Shimmy, are you casting the result as double ? And it automatically does conversion ? – Zo Has Dec 27 '10 at 05:38
  • 2
    @Damien, yes, and note, that for these puroposes (you won't feel anything unless you do a billion of records), firstly because double is faster, second, because passing a string format to the ToString function cost more performance than not passing params. again, you won't feel anything unless you work on gazillions of records. – Shimmy Weitzhandler Dec 27 '10 at 06:06
  • Again, you are still doing a ToString() in the end, so would specifying a 'G' in that change anything performance wise ? – Zo Has Dec 27 '10 at 06:26
  • 1
    You don't have to specify G, cuz `double.ToString` removes trailing zeros by default. – Shimmy Weitzhandler Dec 27 '10 at 07:32
  • 4
    Watch out for values like 0.000001. These will be presented in the exponential notation. double.Parse("0.00000001",System.Globalization.CultureInfo.GetCultureInfo("en-US")).ToString() will give "1E-08" as the result – Konrad Aug 31 '11 at 14:55
  • 1
    This is problematic if the number of figures _before_ the trailing zeroes exceeds 15. That's because the precision of a `double` is less than that of a `decimal`. Example: `source = 123123.2224446668882224440000m;` We want to remove only the four last digits, but because of the inferior precision of `double` in this case, the technique of this answer removes "too much". – Jeppe Stig Nielsen Aug 26 '13 at 12:17
  • 20
    Don't EVER do this. Usually, the reason you have a decimal is because you are representing a number precisely (not approximately like a double does). Granted, this floating point error is probably pretty small, but could result in displaying incorrect numbers, none-the-less – Adam Tegen Sep 13 '13 at 15:37
  • 2
    Ouch, converting a 128-bit datatype (decimal) to a 64-bit datatype (double) for formatting, is not a good idea! – avl_sweden Jul 11 '14 at 06:54
  • Sure this might be bad if you need a ton of precision, but for my problem (only one decimal place) it worked wonderfully! – Bobo Jul 25 '14 at 18:01
1

Trying to do more friendly solution of DecimalToString (https://stackoverflow.com/a/34486763/3852139):

private static decimal Trim(this decimal value)
{
    var s = value.ToString(CultureInfo.InvariantCulture);
    return s.Contains(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator)
        ? Decimal.Parse(s.TrimEnd('0'), CultureInfo.InvariantCulture)
        : value;
}

private static decimal? Trim(this decimal? value)
{
    return value.HasValue ? (decimal?) value.Value.Trim() : null;
}

private static void Main(string[] args)
{
    Console.WriteLine("=>{0}", 1.0000m.Trim());
    Console.WriteLine("=>{0}", 1.000000023000m.Trim());
    Console.WriteLine("=>{0}", ((decimal?) 1.000000023000m).Trim());
    Console.WriteLine("=>{0}", ((decimal?) null).Trim());
}

Output:

=>1
=>1.000000023
=>1.000000023
=>
Dmitri
  • 373
  • 3
  • 3
1

how about this:

public static string TrimEnd(this decimal d)
    {
        string str = d.ToString();
        if (str.IndexOf(".") > 0)
        {
            str = System.Text.RegularExpressions.Regex.Replace(str.Trim(), "0+?$", " ");
            str = System.Text.RegularExpressions.Regex.Replace(str.Trim(), "[.]$", " ");
        }
        return str;
    }
Dannyg
  • 91
  • 1
  • 2
0

You can just set as:

decimal decNumber = 23.45600000m;
Console.WriteLine(decNumber.ToString("0.##"));
0

The following code could be used to not use the string type:

int decimalResult = 789.500
while (decimalResult>0 && decimalResult % 10 == 0)
{
    decimalResult = decimalResult / 10;
}
return decimalResult;

Returns 789.5

Raul Minon
  • 19
  • 4
0

In case you want to keep decimal number, try following example:

number = Math.Floor(number * 100000000) / 100000000;
phuongnd
  • 1,229
  • 15
  • 33
0

Here is an Extention method I wrote, it also removes dot or comma if it`s the last character (after the zeros were removed):

public static string RemoveZeroTail(this decimal num)
{
    var result = num.ToString().TrimEnd(new char[] { '0' });
    if (result[result.Length - 1].ToString() == "." || result[result.Length - 1].ToString() == ",")
    {
        return result.Substring(0, result.Length - 1);
    }
    else
    {
        return result;
    }
}
Jonathan Applebaum
  • 5,738
  • 4
  • 33
  • 52
0

Additional Answer:

In a WPF Application using XAML you could use

{Binding yourDecimal, StringFormat='#,0.00#######################'}

The above answer will preserve the zero in some situations so you could still return 2.00 for example

{Binding yourDecimal, StringFormat='#,0.#########################'}

If you want to remove ALL trailing zeros, adjust accordingly.

0

The following code will be able to remove the trailing 0's. I know it's the hard way but it works.

private static string RemoveTrailingZeros(string input) 
{
    for (int i = input.Length - 1; i > 0; i-- )
    {
        if (!input.Contains(".")) break;
        if (input[i].Equals('0'))
        {
            input= input.Remove(i);
        }
        else break;
    }
    return input;
}
TupleToni
  • 1
  • 1
-1

To remove trailing zero's from a string variable dateTicks, Use

return new String(dateTicks.Take(dateTicks.LastIndexOf(dateTicks.Last(v => v != '0')) + 1).ToArray());

praveen
  • 1
  • 1
-1
string.Format("{0:G29}", decimal.Parse("2.00"))

enter image description here

string.Format("{0:G29}", decimal.Parse(Your_Variable))
M Komaei
  • 7,006
  • 2
  • 28
  • 34
  • How is it different from the [accepted answer](https://stackoverflow.com/a/4525983/11683) posted 13 years ago? – GSerg May 03 '23 at 08:35
-3

try this code:

string value = "100";
value = value.Contains(".") ? value.TrimStart('0').TrimEnd('0').TrimEnd('.') : value.TrimStart('0');
Raging Bull
  • 18,593
  • 13
  • 50
  • 55
Raju
  • 29
  • 2
-3

Very simple answer is to use TrimEnd(). Here is the result,

double value = 1.00;
string output = value.ToString().TrimEnd('0');

Output is 1 If my value is 1.01 then my output will be 1.01

Raj De Inno
  • 1,092
  • 2
  • 14
  • 33
-11

try like this

string s = "2.4200";

s = s.TrimStart("0").TrimEnd("0", ".");

and then convert that to float

SSpoke
  • 5,656
  • 10
  • 72
  • 124
Singleton
  • 3,701
  • 3
  • 24
  • 37
  • 1
    you can use `subString()` method for that, but according to question posted here, i don't see any requirement like that =) – Singleton Dec 24 '10 at 11:03
  • 2
    What about locales that don't use '.' – Dave Hillier Jan 31 '11 at 11:57
  • 10
    This fails miserably for numbers which are an even multiple of 10.0. – Ben Voigt Feb 21 '11 at 15:00
  • 1
    What @BenVoigt said is entirely correct, as an examle `"12300.00"` will be trimmed to `"123"`. Another effect that seems undesirable is that some numbers, like `"0.00"` are trimmed all the way down to the empty string `""` (although this number ___is___ a special case of being an integral "multiple of 10.0"). – Jeppe Stig Nielsen Aug 26 '13 at 12:35