391

I have got a price field to display which sometimes can be either 100 or 100.99 or 100.9, What I want is to display the price in 2 decimal places only if the decimals are entered for that price , for instance if its 100 so it should only show 100 not 100.00 and if the price is 100.2 it should display 100.20 similarly for 100.22 should be same . I googled and came across some examples but they didn't match exactly what i wanted :

// just two decimal places
String.Format("{0:0.00}", 123.4567);      // "123.46"
String.Format("{0:0.00}", 123.4);         // "123.40"
String.Format("{0:0.00}", 123.0);         // "123.00"
Cœur
  • 37,241
  • 25
  • 195
  • 267
Mr A
  • 6,448
  • 25
  • 83
  • 137
  • 4
    possible duplicate of [.net Format decimal to two places or a whole number](http://stackoverflow.com/questions/2148271/net-format-decimal-to-two-places-or-a-whole-number) – Binary Worrier Aug 16 '11 at 10:41
  • 2
    RE: "What I want is to display the price in 2 decimal places only if the decimals are entered for that price" -- so if the user types "100.00" you want to show "100.00", but if they type "100" you only want to show "100"? -- number types only track the value of the number -- not which of the insignificant digits were entered by a user and which were not -- for that you will need to use a string. – BrainSlugs83 Dec 10 '13 at 22:26
  • 2
    @BinaryWorrier I think that this question may be a duplicate, but it has much better and more complete answers. IMO the other one should be marked as a duplicate of this one. – Ryan Gates Jan 31 '15 at 10:50
  • 3
    just add .Replace(".00","") – Dave Sumter Jun 02 '17 at 09:01
  • 3
    All you need is `value`.ToString("0.##"); – Mehdi Nov 17 '20 at 10:25
  • @Mehdi - Your suggestion would not work for `100.2`. He wants to get `"100.20"`, not `"100.2"`. – ConnorsFan May 15 '21 at 01:39
  • mmm, you're right! I should've read the Q more carefully. – Mehdi May 16 '21 at 22:29

18 Answers18

686

Sorry for reactivating this question, but I didn't find the right answer here.

In formatting numbers you can use 0 as a mandatory place and # as an optional place.

So:

// just two decimal places
String.Format("{0:0.##}", 123.4567);      // "123.46"
String.Format("{0:0.##}", 123.4);         // "123.4"
String.Format("{0:0.##}", 123.0);         // "123"

You can also combine 0 with #.

String.Format("{0:0.0#}", 123.4567)       // "123.46"
String.Format("{0:0.0#}", 123.4)          // "123.4"
String.Format("{0:0.0#}", 123.0)          // "123.0"

For this formating method is always used CurrentCulture. For some Cultures . will be changed to ,.

Answer to original question:

The simpliest solution comes from @Andrew (here). So I personally would use something like this:

var number = 123.46;
String.Format(number % 1 == 0 ? "{0:0}" : "{0:0.00}", number)
Community
  • 1
  • 1
Gh61
  • 9,222
  • 4
  • 28
  • 39
  • 23
    At first, I thought this should be the answer, until I re-read the original question multiple times. The OP is not entirely clear what he exactly wants, but it seems he always wants 2 decimal places if someone enters a fraction. So if someone entered 1.1 then he'd want 1.10; this code wouldn't do that. – Doug S May 27 '14 at 01:45
  • 47
    Oops, I read it again and you're right. So, this isn't the right answer but at least someone might find this useful. – Gh61 May 29 '14 at 17:11
  • 3
    What OP needed can be achieved with this: http://stackoverflow.com/a/33180829/2321042 – Andrew Jul 14 '16 at 21:53
  • I just found it useful, and (somewhat) matching what a BoundField in a GridView does with a SqlDouble and no format instruction. You have to indicate the max# you'll show. (Vs. BoundField, happy to show as many or as few as you like) – fortboise Dec 30 '16 at 17:32
178

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.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • 10
    This isn't really the question that was asked -- but had it been -- why not just use string.Format("{0:0.00}").Replace(".00", "")? – BrainSlugs83 Dec 10 '13 at 22:28
  • 24
    @BrainSlugs83: depending on the current thread's `CurrentCulture`, decimal separator might not be `.`. Unless `CultureInfo.InvariantCulture` is used with `string.Format`, you would have to check the value of `CultureInfo.NumberFormat.NumberDecimalSeparator`, and that would be a real PITA. :) – vgru Jan 15 '14 at 13:08
  • @Uwe Keim What if i have `60000` int and I want it to be `60.000`? – Prashant Pimpale Mar 20 '19 at 06:53
  • 2
    This answer is a case of "reinventing a square wheel". Does not take into account culture or the fact that this has already been handled by .NET. – bytedev Apr 13 '20 at 07:01
93

This is a common formatting floating number use case.

Unfortunately, all of the built-in one-letter format strings (eg. F, G, N) won't achieve this directly.
For example, num.ToString("F2") will always show 2 decimal places like 123.40.

You'll have to use 0.## pattern even it looks a little verbose.

A complete code example:

double a = 123.4567;
double b = 123.40;
double c = 123.00;

string sa = a.ToString("0.##"); // 123.46
string sb = b.ToString("0.##"); // 123.4
string sc = c.ToString("0.##"); // 123
detale
  • 12,482
  • 4
  • 41
  • 42
67

Old question but I wanted to add the simplest option in my opinion.

Without thousands separators:

value.ToString(value % 1 == 0 ? "F0" : "F2")

With thousands separators:

value.ToString(value % 1 == 0 ? "N0" : "N2")

The same but with String.Format:

String.Format(value % 1 == 0 ? "{0:F0}" : "{0:F2}", value) // Without thousands separators
String.Format(value % 1 == 0 ? "{0:N0}" : "{0:N2}", value) // With thousands separators

If you need it in many places, I would use this logic in an extension method:

public static string ToCoolString(this decimal value)
{
    return value.ToString(value % 1 == 0 ? "N0" : "N2"); // Or F0/F2 ;)
}
Andrew
  • 7,602
  • 2
  • 34
  • 42
30

try

double myPrice = 123.0;

String.Format(((Math.Round(myPrice) == myPrice) ? "{0:0}" : "{0:0.00}"), myPrice);
Yahia
  • 69,653
  • 9
  • 115
  • 144
14

If your program needs to run quickly, call value.ToString(formatString) for ~35% faster string formatting performance relative to $"{value:formatString}" and string.Format(formatString, value).

Data

C# String Formatting Performance - VS2017 15.4.5

Code

using System;
using System.Diagnostics;

public static class StringFormattingPerformance
{
   public static void Main()
   {
      Console.WriteLine("C# String Formatting Performance");
      Console.WriteLine("Milliseconds Per 1 Million Iterations - Best Of 5");
      long stringInterpolationBestOf5 = Measure1MillionIterationsBestOf5(
          (double randomDouble) =>
          {
             return $"{randomDouble:0.##}";
          });
      long stringDotFormatBestOf5 = Measure1MillionIterationsBestOf5(
          (double randomDouble) =>
          {
             return string.Format("{0:0.##}", randomDouble);
          });
      long valueDotToStringBestOf5 = Measure1MillionIterationsBestOf5(
          (double randomDouble) =>
          {
             return randomDouble.ToString("0.##");
          });
      Console.WriteLine(
$@"            $""{{value:formatString}}"": {stringInterpolationBestOf5} ms
 string.Format(formatString, value): {stringDotFormatBestOf5} ms
       value.ToString(formatString): {valueDotToStringBestOf5} ms");
   }

   private static long Measure1MillionIterationsBestOf5(
       Func<double, string> formatDoubleUpToTwoDecimalPlaces)
   {
      long elapsedMillisecondsBestOf5 = long.MaxValue;
      for (int perfRunIndex = 0; perfRunIndex < 5; ++perfRunIndex)
      {
         var random = new Random();
         var stopwatch = Stopwatch.StartNew();
         for (int i = 0; i < 1000000; ++i)
         {
            double randomDouble = random.NextDouble();
            formatDoubleUpToTwoDecimalPlaces(randomDouble);
         }
         stopwatch.Stop();
         elapsedMillisecondsBestOf5 = Math.Min(
            elapsedMillisecondsBestOf5, stopwatch.ElapsedMilliseconds);
      }
      return elapsedMillisecondsBestOf5;
   }
}

Code Output

C# String Formatting Performance
Milliseconds Per 1 Million Iterations - Best Of 5
            $"{value:formatString}": 419 ms
 string.Format(formatString, value): 419 ms
       value.ToString(formatString): 264 ms

References

Custom Numeric Format Strings [learn.microsoft.com]

Qt Charts BarChart Example [doc.qt.io]

Neil Justice
  • 1,325
  • 2
  • 12
  • 25
  • Great to know it can be done within the $string notation. And if it's not used in a tight loop, then no worries about the performance hit. – Paul Masri-Stone Oct 20 '20 at 09:55
  • This is mainly due to boxing and other extra allocations. See [this answer](https://stackoverflow.com/a/63904747/419761) for more details. – l33t Jun 17 '21 at 12:59
9

I don't know of anyway to put a condition in the format specifier, but you can write your own formatter:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
               // all of these don't work
            Console.WriteLine("{0:C}", 10);
            Console.WriteLine("{0:00.0}", 10);
            Console.WriteLine("{0:0}", 10);
            Console.WriteLine("{0:0.00}", 10);
            Console.WriteLine("{0:0}", 10.0);
            Console.WriteLine("{0:0}", 10.1);
            Console.WriteLine("{0:0.00}", 10.1);

          // works
            Console.WriteLine(String.Format(new MyFormatter(),"{0:custom}", 9));
            Console.WriteLine(String.Format(new MyFormatter(),"{0:custom}", 9.1));
            Console.ReadKey();
        }
    }

    class MyFormatter : IFormatProvider, ICustomFormatter
    {
        public string Format(string format, object arg, IFormatProvider formatProvider)
        {
            switch (format.ToUpper())
            {
                case "CUSTOM":
                    if (arg is short || arg is int || arg is long)
                        return arg.ToString();
                    if (arg is Single || arg is Double)
                        return String.Format("{0:0.00}",arg);
                    break;
                // Handle other
                default:
                    try
                    {
                        return HandleOtherFormats(format, arg);
                    }
                    catch (FormatException e)
                    {
                        throw new FormatException(String.Format("The format of '{0}' is invalid.", format), e);
                    }
            }
            return arg.ToString(); // only as a last resort
        }

        private string HandleOtherFormats(string format, object arg)
        {
            if (arg is IFormattable)
                return ((IFormattable)arg).ToString(format, CultureInfo.CurrentCulture);
            if (arg != null)
                return arg.ToString();
            return String.Empty;
        }

        public object GetFormat(Type formatType)
        {
            if (formatType == typeof(ICustomFormatter))
                return this;
            return null;
        }
    }
}
Tim Hoolihan
  • 12,316
  • 3
  • 41
  • 54
7

Here is an alternative to Uwe Keim's method, which would still maintain the same method call:

var example1 = MyCustomFormat(123.1);  // Output: 123.10
var example2 = MyCustomFormat(123.95); // Output: 123.95
var example3 = MyCustomFormat(123);    // Output: 123

With MyCustomFormat being something like:

public static string MyCustomFormat( double myNumber )
{
    var str (string.Format("{0:0.00}", myNumber))
    return (str.EndsWith(".00") ? str.Substring(0, strLastIndexOf(".00")) : str;
}
Christian
  • 1,017
  • 3
  • 14
  • 30
Steve
  • 7,747
  • 5
  • 31
  • 44
  • This didn't work for me as it seems TrimEnd takes an array of chars like {',', '.', ' '} rather than a string like ".00" - See http://msdn.microsoft.com/en-us/library/system.string.trimend.aspx – user1069816 Dec 04 '13 at 15:28
  • You're right - not sure how I missed that. I've updated to work correctly. – Steve Dec 05 '13 at 15:29
  • 5
    Depending on the current thread's `CurrentCulture`, decimal separator might not be `.`. Unless `CultureInfo.InvariantCulture` is used with `string.Format`, you would have to check the value of `CultureInfo.NumberFormat.NumberDecimalSeparator`, which is rather inelegant. – vgru Jan 15 '14 at 13:09
7

Simple one line code :

public static string DoFormat(double myNumber)
{
    return string.Format("{0:0.00}", myNumber).Replace(".00","");
}
Philip Stuyck
  • 7,344
  • 3
  • 28
  • 39
  • The problem with this is if it is run where the decimal separator is a comma. Check the comments for [this answer](https://stackoverflow.com/a/6951366/2321042). – Andrew Feb 27 '19 at 13:23
5

Try:

String.Format("{0:0.00}", Convert.ToDecimal(totalPrice));
Prashant Pimpale
  • 10,349
  • 9
  • 44
  • 84
Matei Beloiu
  • 61
  • 1
  • 3
  • 1
    This will fail on a culture where point is used as a thousands separator. I just tried it now. – sergiol May 10 '22 at 13:22
5

I am afraid there is no built-in format that will do this. You will have to use a different format depending on whether the value is a whole number or not. Or always format to 2 decimal places, and manipulate the string afterwards to remove any trailing ".00".

Nikki Locke
  • 2,759
  • 6
  • 29
  • 53
4

If none of the other answers work for you, it may be because you are binding the ContentProperty of a control in the OnLoad function, which means this won't work:

private void UserControl_Load(object sender, RoutedEventArgs e)
{
  Bind.SetBindingElement(labelName, String.Format("{0:0.00}", PropertyName), Label.ContentProperty) 
}

The solution is simple: there is a ContentStringFormat property in the xaml. So when you create the label do this:

//if you want the decimal places definite
<Label Content="0" Name="labelName" ContentStringFormat="0.00"/>

Or

//if you want the decimal places to be optional
<Label Content="0" Name="labelName" ContentStringFormat="0.##"/>
Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
Brazizzle
  • 41
  • 2
4

Try this:

var number = 123.4567;
var str = number.ToString("N2");
Sepideh I
  • 71
  • 4
3

something like this will work too:

String.Format("{0:P}", decimal.Parse(Resellers.Fee)).Replace(".00", "")
ekkis
  • 9,804
  • 13
  • 55
  • 105
3

When dealing with decimals coming from a (T-)SQL database, you want to be able to convert nullable and non-nullable decimals with x decimal places and be able to review the code easily against your table definitions - and of course, display the right number of decimals to the user.

Unfortunately, the Entity Framework doesn't automatically convert something like a SQL decimal(18,2) into a .NET equivalent with the same number of decimal places (since there is only decimal with full precision available). You have to truncate the decimal places manually.

So, I did it this way:

public static class Extensions
{
    public static string ToStringDecimal(this decimal d, byte decimals)
    {
        var fmt = (decimals>0) ? "0." + new string('0', decimals) : "0";
        return d.ToString(fmt);
    }

    public static string ToStringDecimal(this decimal? d, byte decimals)
    {
        if (!d.HasValue) return "";
        return ToStringDecimal(d.Value, decimals);
    }
}

Example Usage:

void Main()
{
    decimal d = (decimal)1.2345;
    decimal? d2 = null; 

    Console.WriteLine(d.ToStringDecinal(2)); // prints: "1.23" (2 decimal places)
    Console.WriteLine(d.ToStringDecinal(0)); // prints: "1" (show integer number)
    Console.WriteLine(d2.ToStringDecimal(2)); // prints: "" (show null as empty string)
}
Matt
  • 25,467
  • 18
  • 120
  • 187
2

To make the code more clear that Kahia wrote in (it is clear but gets tricky when you want to add more text to it)...try this simple solution.

if (Math.Round((decimal)user.CurrentPoints) == user.CurrentPoints)
     ViewBag.MyCurrentPoints = String.Format("Your current Points: {0:0}",user.CurrentPoints);
else
     ViewBag.MyCurrentPoints = String.Format("Your current Points: {0:0.0}",user.CurrentPoints);

I had to add the extra cast (decimal) to have Math.Round compare the two decimal variables.

coding_is_fun
  • 1,021
  • 10
  • 5
1

A recent project has a similar requirement. I wrote this decimal extension method, which uses the currency ("C") Format Specifier. In addition to removing zeros, it also has options for decimal digits precision, currency symbol, separator and culture.

public static DecimalExtension{

     public static string ToCurrency(this decimal val, 
                                     int precision = 2, 
                                     bool currencySymbol = false, 
                                     bool separator = false, 
                                     CultureInfo culture = null)
      {     
         if(culture == null) culture = new CultureInfo("en-US");
                                                   
         NumberFormatInfo nfi = culture.NumberFormat;
         nfi.CurrencyDecimalDigits = precision;
            
         string zeros = new String('0', precision);       
                    
         //Remove zeros
         var result = val.ToString("C",fi).Replace(nfi.CurrencyDecimalSeparator + zeros,"");
                     
         if(!separator) result = result.Replace(nfi.CurrencyGroupSeparator,"");
                    
         return currencySymbol? result: result.Replace(nfi.CurrencySymbol,"");      
        }   
}

Examples:

decimal Total = 123.00M;
Console.WriteLine(Total.ToCurrency());  
//output: 123

decimal Total = 1123.12M;
Console.WriteLine(Total.ToCurrency()); 
//Output:  1123.12

Console.WriteLine(Total.ToCurrency(4));
//Output:  1123.1200

Console.WriteLine(Total.ToCurrency(2,true,true));
//output:  $1,123.12
 
CultureInfo culture = new CultureInfo("pt-BR")  //Brazil
Console.WriteLine(Total.ToCurrency(2,true,true, culture));
//output:  R$ 1.123,12
Wei
  • 724
  • 9
  • 10
-1

Try This

string Output = String.Format("{0:0.00}", Decimal.Parse(InputStringValue));