使用字符串格式化显示小数点后两位或简单整数

392

我有一个价格字段需要展示,有时候可能是100或100.99或100.9,我想要的是只有在输入了小数位的情况下才将价格显示为2位小数,例如如果价格是100,那么应该只显示100而不是100.00,如果价格是100.2,则应该显示100.20,对于100.22也是同样的。

我搜索了一些例子,但它们并不完全符合我的要求:

// 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"

4
可能是与 .net 将十进制格式化为两位小数或整数 相似的问题。 - Binary Worrier
2
回复:“我的要求是,仅在输入价格的小数时显示2位小数”——因此,如果用户键入“100.00”,您希望显示“100.00”,但如果他们键入“100”,您只想显示“100”?——数字类型仅跟踪数字的值——而不是用户输入的哪些无关紧要的数字和哪些不是——为此,您需要使用字符串。 - BrainSlugs83
2
@BinaryWorrier 我认为这个问题可能是一个重复的,但它有更好和更完整的答案。在我看来,另一个应该被标记为这个的重复。 - Ryan Gates
3
只需添加.Replace(".00","") - Dave Sumter
3
你只需要使用 value.ToString("0.##"); 即可。 - Mehdi
显示剩余2条评论
18个回答

689

很抱歉重新激活这个问题,但我在这里没有找到正确的答案。

在格式化数字时,您可以使用0作为必填位,使用#作为可选位。

所以:

// 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"

您也可以将0#组合使用。

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"

对于这种格式化方法,通常使用CurrentCulture。对于某些文化,.将被更改为,

原始问题的答案:

最简单的解决方案来自 @Andrew(这里)。因此,我个人会使用类似以下的内容:

var number = 123.46;
String.Format(number % 1 == 0 ? "{0:0}" : "{0:0.00}", number)

23
起初,我认为这应该是答案,直到我多次重新阅读原问题。OP并不十分清楚他到底想要什么,但似乎他总是希望在有人输入分数时保留2位小数。所以如果有人输入1.1,他希望得到的是1.10;这段代码无法做到这一点。 - Doug S
47
抱歉,我再次阅读了它,你是对的。 所以,这不是正确的答案,但至少有人可能会发现这个有用。 - Gh61
3
这个链接可以实现OP所需的内容:https://dev59.com/7Ww05IYBdhLWcg3w6F8w#33180829 - Andrew
我刚发现这很有用,并且(在某种程度上)与GridView中的BoundField使用SqlDouble且没有格式指令相匹配。您必须指示要显示的最大数量。(与BoundField相比,可以根据需要显示多少个或少数个) - fortboise

178
一个不太优雅的方法是:
var my = DoFormat(123.0);

假设 DoFormat 是这样的:

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;
    }
}

虽然不太优雅,但在我参与的一些项目中,在类似的情况下它对我有用。


10
虽然这不是原来提出的问题,但如果是的话,为什么不直接使用 string.Format("{0:0.00}").Replace(".00", "") 呢? - BrainSlugs83
25
根据当前线程的“CurrentCulture”,十进制分隔符可能不是“.”。除非在使用string.Format时使用了CultureInfo.InvariantCulture,否则您需要检查CultureInfo.NumberFormat.NumberDecimalSeparator的值,这将是真正麻烦的事情。 :) - vgru
@Uwe Keim 如果我有 60000 个整数,我想要它变成 60.000 怎么办? - Prashant Pimpale
2
这个答案是“重新发明轮子”的一个例子。它没有考虑到文化或者已经被.NET处理过的事实。 - bytedev

94

这是一个常见的浮点数格式化用例。

不幸的是,内置的单字母格式字符串(例如 F、G、N)都不能直接实现此目的。
例如,num.ToString("F2") 将始终显示 2 位小数,如 123.40

您必须使用0.##模式,即使它看起来有点冗长。

完整的代码示例:

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

12
他想要的是123.40,而不是123.4。 - Andrew
15
不解决这个问题,而是解决我的问题。我点赞让大家都能看到它。 - Emad

67

虽然是老问题,但我想在我的观点中加入最简单的选项。

不带千位分隔符:

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

千位分隔符:

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

同样的事情,但使用 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
如果您需要在多个地方使用它,我会在一个扩展方法中使用这个逻辑:
public static string ToCoolString(this decimal value)
{
    return value.ToString(value % 1 == 0 ? "N0" : "N2"); // Or F0/F2 ;)
}

30

尝试

double myPrice = 123.0;

String.Format(((Math.Round(myPrice) == myPrice) ? "{0:0}" : "{0:0.00}"), myPrice);

6
string.Format((number % 1) == 0 ? "{0:0}" : "{0:0.00}", number); 翻译为:将数字格式化为整数或保留两位小数的字符串表达式。如果数字是整数,则格式化为不带小数点的整数形式;如果数字具有小数部分,则格式化为保留两位小数的形式。 - Patrick

14

如果您的程序需要快速运行,则调用value.ToString(formatString)可以带来大约35%更快的字符串格式化性能,相对于 $"{value:formatString}" 和 string.Format(formatString, value)。

数据

C# String Formatting Performance - VS2017 15.4.5

代码

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;
   }
}

代码输出

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

参考文献

自定义数值格式字符串 [learn.microsoft.com]

Qt图表条形图示例 [doc.qt.io]


很高兴知道它可以在$string符号内完成。如果它没有在紧密的循环中使用,那么就不用担心性能问题了。 - Paul Masri-Stone
这主要是由于装箱和其他额外的分配。有关更多详细信息,请参见此答案 - l33t

9

我不知道有什么方法可以在格式说明符中放置条件,但您可以编写自己的格式化程序:

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;
        }
    }
}

7
这里提供一种替代Uwe Keim方法的方案,仍然保持相同的方法调用:
var example1 = MyCustomFormat(123.1);  // Output: 123.10
var example2 = MyCustomFormat(123.95); // Output: 123.95
var example3 = MyCustomFormat(123);    // Output: 123

使用类似于MyCustomFormat的格式:
public static string MyCustomFormat( double myNumber )
{
    var str (string.Format("{0:0.00}", myNumber))
    return (str.EndsWith(".00") ? str.Substring(0, strLastIndexOf(".00")) : str;
}

这对我没有起作用,因为TrimEnd似乎需要一个字符数组,如{',', '.', ' '},而不是像“.00”这样的字符串 - 请参阅http://msdn.microsoft.com/en-us/library/system.string.trimend.aspx。 - user1069816
你说得对 - 不确定我怎么会错过那个。我已经更新以使其正常工作。 - Steve
5
根据当前线程的CurrentCulture,十进制分隔符可能不是。除非在string.Format中使用了CultureInfo.InvariantCulture,否则您需要检查CultureInfo.NumberFormat.NumberDecimalSeparator的值,这样做相对而言比较繁琐。 - vgru

7

简单的一行代码:

public static string DoFormat(double myNumber)
{
    return string.Format("{0:0.00}", myNumber).Replace(".00","");
}

这个问题在小数分隔符是逗号的情况下运行时会出现。请查看此回答中的评论。 - Andrew

5

尝试:

String.Format("{0:0.00}", Convert.ToDecimal(totalPrice));

1
这在使用点作为千位分隔符的文化中会失败。我刚刚尝试了一下。 - sergiol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接