数以百万计的字符串格式,四舍五入的数以千计

Posted

技术标签:

【中文标题】数以百万计的字符串格式,四舍五入的数以千计【英文标题】:String format numbers to millions, thousands with rounding 【发布时间】:2015-05-12 02:22:24 【问题描述】:

我正在尝试格式化显示价格,我想显示一个带有百万 (M) 或千 (K) 后缀的数字,但最多只能显示 3 个值,向下取整。

我发现this question 非常接近我想要的,但不处理舍入(特别是总是向下舍入)

同样,with this question 您无法控制舍入。

示例输入/预期输出:

1 = 1
10 = 10
100 = 100
1000 = 1K
100000 = 100K
125000 = 125K
125900 = 125K
1000000 = 1M
1250000 = 1.25M
1258000 = 1.25M
10000000 = 10M
10500000 = 10.5M
100000000 = 100M
100100000 = 100M

我基本上只想显示 3 个值。

我看不出如何使用"," custom specifier 并指定舍入。

我最初的想法表明我需要结合使用上述、Math.Floor 和一些 .ToString() 格式化魔法,但我不确定从哪里开始。

谁能帮帮我?

提前致谢。

【问题讨论】:

重复***.com/questions/2134161/… @Steve 无法控制该问题的舍入 我现在如何看待它,您必须定义一些常量并进行一些条件检查以验证数字是否在某个范围内,然后应用正确的“映射”来显示值正确。 @KevinAvignon 是的,肯定需要一些条件来查看它属于哪个范围,但我一直在弄清楚如何进行舍入 似乎史蒂夫发布的链接中的代码总是四舍五入,所以如果你从数字中得到-500(或相对于你试图四舍五入的数字),它看起来好像它的四舍五入,不是吗? 【参考方案1】:

结合您链接到的其他问题中的一种格式化技术,这应该会有所帮助。

  internal long MaxThreeSignificantDigits(long x)
  
     int i = (int)Math.Log10(x);
     i = Math.Max(0, i - 2);
     i = (int)Math.Pow(10, i);
     return x / i * i;
  

编辑:

好的,这个怎么样?

 Console.WriteLine(SO30180672.FormatNumber(1));
 Console.WriteLine(SO30180672.FormatNumber(12));
 Console.WriteLine(SO30180672.FormatNumber(123));
 Console.WriteLine(SO30180672.FormatNumber(1234));
 Console.WriteLine(SO30180672.FormatNumber(12345));
 Console.WriteLine(SO30180672.FormatNumber(123456));
 Console.WriteLine(SO30180672.FormatNumber(1234567));
 Console.WriteLine(SO30180672.FormatNumber(12345678));
 Console.WriteLine(SO30180672.FormatNumber(123456789));

以下部分从这里复制:https://***.com/a/23384710/253938

   internal class SO30180672
   
      internal static string FormatNumber(long num)
      
         num = MaxThreeSignificantDigits(num);

         if (num >= 100000000)
            return (num / 1000000D).ToString("0.#M");
         if (num >= 1000000)
            return (num / 1000000D).ToString("0.##M");
         if (num >= 100000)
            return (num / 1000D).ToString("0k");
         if (num >= 100000)
            return (num / 1000D).ToString("0.#k");
         if (num >= 1000)
            return (num / 1000D).ToString("0.##k");
         return num.ToString("#,0");
      


      internal static long MaxThreeSignificantDigits(long x)
      
         int i = (int)Math.Log10(x);
         i = Math.Max(0, i - 2);
         i = (int)Math.Pow(10, i);
         return x / i * i;
      
   

编辑 2 - 非常感谢@Rhexis

   internal class SO30180672
   
      internal static void RunTest()
      
         Console.WriteLine(FormatNumber(1));
         Console.WriteLine(FormatNumber(10));
         Console.WriteLine(FormatNumber(100));
         Console.WriteLine(FormatNumber(1000));
         Console.WriteLine(FormatNumber(10000));
         Console.WriteLine(FormatNumber(100000));
         Console.WriteLine(FormatNumber(125000));
         Console.WriteLine(FormatNumber(125900));
         Console.WriteLine(FormatNumber(1000000));
         Console.WriteLine(FormatNumber(1250000));
         Console.WriteLine(FormatNumber(1258000));
         Console.WriteLine(FormatNumber(10000000));
         Console.WriteLine(FormatNumber(10500000));
         Console.WriteLine(FormatNumber(100000000));
         Console.WriteLine(FormatNumber(100100000));
      

      private static string FormatNumber(long num)
      
         // Ensure number has max 3 significant digits (no rounding up can happen)
         long i = (long)Math.Pow(10, (int)Math.Max(0, Math.Log10(num) - 2));
         num = num / i * i;

         if (num >= 1000000000)
            return (num / 1000000000D).ToString("0.##") + "B";
         if (num >= 1000000)
            return (num / 1000000D).ToString("0.##") + "M";
         if (num >= 1000)
            return (num / 1000D).ToString("0.##") + "K";

         return num.ToString("#,0");
      
   

【讨论】:

您能否提供更多信息,例如如何将其与其他答案结合使用?不知道如何使用这个 我相信@RenniePet 有答案。我确实认为你只需要 3 if's in 'FormatNumber'。使用您的代码,我得到了所需的输出。 (见这里:pastebin.com/8qXHMvQG) @Rhesis:好主意。不过,我不会为改变我的答案而烦恼——关于获得最多 3 个有效数字的问题引起了我的兴趣。我记得很久很久以前学习如何做到这一点。 (40 年前,如果你能相信的话。) @Rhexis:好的,现在我已经添加了您提出的改进建议——非常感谢。 @RenniePet 你的代码适用于我的测试,我会用这个。谢谢!【参考方案2】:

这是我的测试输出代码

1249            1.24K
12499           12.4K
124999          124K
1249999         1.24M
12499999        12.4M
124999999       124M
1249999999      1.24B

代码最多输出三位数。

    static string FormatNumber(uint n)
    
        if (n < 1000)
            return n.ToString();

        if (n < 10000)
            return String.Format("0:#,.##K", n - 5);

        if (n < 100000)
            return String.Format("0:#,.#K", n - 50);

        if (n < 1000000)
            return String.Format("0:#,.K", n - 500);

        if (n < 10000000)
            return String.Format("0:#,,.##M", n - 5000);

        if (n < 100000000)
            return String.Format("0:#,,.#M", n - 50000);

        if (n < 1000000000)
            return String.Format("0:#,,.M", n - 500000);

        return String.Format("0:#,,,.##B", n - 5000000);
    

【讨论】:

Bravo - 不到我的答案包含的代码行数。 非常接近...只需要向下取整。我想我需要像上面提到的@Rhexis 那样取消 500 等才能四舍五入 我需要四舍五入,你的答案更简单,不用Math.【参考方案3】:

由于格式本质上会根据范围发生变化,因此您很可能需要一些类似于下面的条件格式。我只测试了提供的样本集,因此请确保它适用于所有预期值。

class Program

    static void Main(String[] args)
    
        Console.WriteLine(RoundAndFormat(1));
        Console.WriteLine(RoundAndFormat(10));
        Console.WriteLine(RoundAndFormat(100));
        Console.WriteLine(RoundAndFormat(1000));
        Console.WriteLine(RoundAndFormat(100000));
        Console.WriteLine(RoundAndFormat(125000));
        Console.WriteLine(RoundAndFormat(125900));
        Console.WriteLine(RoundAndFormat(1000000));
        Console.WriteLine(RoundAndFormat(1250000));
        Console.WriteLine(RoundAndFormat(1258000));
        Console.WriteLine(RoundAndFormat(10000000));
        Console.WriteLine(RoundAndFormat(10500000));
        Console.WriteLine(RoundAndFormat(100000000));
        Console.WriteLine(RoundAndFormat(100100000));

        Console.ReadLine();
    

    public static String RoundAndFormat(Int32 value)
    
        var result = String.Empty;
        var negative = value < 0;
        if (negative) value = value * -1;

        if (value < 1000)
        
            result = value.ToString();
        
        else if (value < 1000000)
        
            result = RoundDown(value / 1000.0, 0) + "K";
        
        else if (value < 100000000)
        
            result = RoundDown(value / 1000000.0, 2) + "M";
        
        else if (value < 10000000000)
        
            result = RoundDown(value / 1000000.0, 0) + "M";
        

        if (negative) return "-" + result;
        return result;
    

    public static Double RoundDown(Double value, Int32 digits)
    
        var pow = Math.Pow(10, digits);
        return Math.Truncate(value * pow) / pow;
    

【讨论】:

快速更改以处理负值。 不应该是 Math.Truncate(value / pow) * pow; ? 不,该函数是 sig fig 计算。我将小数点向右移动 x 位,删除小数部分,并将小数点向后移动。我将使用小数点后 1.2580 的示例:pow = 10^2 = 100, Truncate(1.2580 * 100) = 125, 125.80 / 100 = 1.25【参考方案4】:

感谢大家的帮助,这让我走上了自己解决问题的正确轨道。

public static string FormatPriceValue(this int num)

    if (num >= 100000000)
    
        return ((num >= 10050000 ? num - 500000 : num) / 1000000D).ToString("#M");
    
    if (num >= 10000000)
    
        return ((num >= 10500000 ? num - 50000 : num) / 1000000D).ToString("0.#M");
    
    if (num >= 1000000)
    
        return ((num >= 1005000 ? num-5000 : num) / 1000000D).ToString("0.##M");
    
    if (num >= 100000)
    
        return ((num >= 100500 ? num - 500 : num) / 1000D).ToString("0.k");
    
    if (num >= 10000)
    
        return ((num >= 10550 ? num - 50 : num) / 1000D).ToString("0.#k");
    

    return num >= 1000 ? ((num >= 1005 ? num - 5 : num) / 1000D).ToString("0.##k") : num.ToString("#,0");

【讨论】:

请再看看我的答案,@Rhesis 提出了改进建议。与其考虑向上或向下“四舍五入”,不如考虑“最多 3 个有效数字”。这样做的代码有点神秘,但是一旦你有了它,你就不需要很多你正在使用的“幻数”——当程序需要在未来。 @RenniePet 我已经用我当前的代码通过了所有这些单元测试,所以我会插入你的,看看它们是否仍然通过。敬请期待:) 太好了,很高兴我能帮上忙。但是大部分的荣誉实际上应该归@Rhexis,我只做了有效数字部分,是Rhexis修复了格式部分。【参考方案5】:

我知道这是一个老问题,但我不禁注意到这些答案中的大多数都无法扩展,它们中的大多数都是非常相似的代码行,为了更多的值而重复。我需要一些可以处理更大值的东西,所以我尝试以一种聪明的方式使用数学而不是重复的代码。这确实需要使用浮点数学(双精度),因为 unsigned long 的最大值仅为 1.84e+19。我的解决方案:

public static string GetStringRepresentation(double count)

    string tokens = " KMBtqQsSondUDT"; //Infinitely expandable (at least to the limit of double floating point values)
    for (double i = 1; true; i += 1)
    
        double val = Math.Pow(1000, i);
        if (val > count)
        
            return $"count / Math.Pow(1000, i - 1)tokens[(int)i - 1]".Trim();
        
    

测试代码:

Console.WriteLine("                     1: " + GetStringRepresentation(                  1).PadLeft(7, ' '));
Console.WriteLine("                    12: " + GetStringRepresentation(                 12).PadLeft(7, ' '));
Console.WriteLine("                   123: " + GetStringRepresentation(                123).PadLeft(7, ' '));
Console.WriteLine("                  1230: " + GetStringRepresentation(               1230).PadLeft(7, ' '));
Console.WriteLine("                 12340: " + GetStringRepresentation(              12340).PadLeft(7, ' '));
Console.WriteLine("                123450: " + GetStringRepresentation(             123450).PadLeft(7, ' '));
Console.WriteLine("               1230000: " + GetStringRepresentation(            1230000).PadLeft(7, ' '));
Console.WriteLine("              12340000: " + GetStringRepresentation(           12340000).PadLeft(7, ' '));
Console.WriteLine("             123450000: " + GetStringRepresentation(          123450000).PadLeft(7, ' '));
Console.WriteLine("            1230000000: " + GetStringRepresentation(         1230000000).PadLeft(7, ' '));
Console.WriteLine("         1230000000000: " + GetStringRepresentation(      1230000000000).PadLeft(7, ' '));
Console.WriteLine("      1230000000000000: " + GetStringRepresentation(   1230000000000000).PadLeft(7, ' '));
Console.WriteLine("   1230000000000000000: " + GetStringRepresentation(1230000000000000000).PadLeft(7, ' '));

输出:

                     1:       1
                    12:      12
                   123:     123
                  1230:   1,23K
                 12340:  12,34K
                123450: 123,45K
               1230000:   1,23M
              12340000:  12,34M
             123450000: 123,45M
            1230000000:   1,23B
         1230000000000:   1,23t
      1230000000000000:   1,23q
   1230000000000000000:   1,23Q

【讨论】:

【参考方案6】:

下面是执行此操作的 VBS/VbScript 函数。 NB => VB 和 VBA 提供直接格式调用。

' 用法:使用大于千位的符号返回一个数字(例如:234,789,145 = 234.8M)

函数格式_Large_Number(nb, nbDigit)

' Safety
If Not IsNumeric(nb) Then FormatLargeNumber = nb : Exit Function
If nbDigit < 1 Then nbDigit = 1
If nbDigit > 3 Then nbDigit = 3

' Initiate var
Dim rtnNb    : rtnNb    = nb
Dim nbFormat : nbFormat = FormatCurrency(rtnNb, 0, vbTrue, vbTrue)
               nbFormat = Trim(Replace(nbFormat, "$", ""))
Dim nbSymbol : nbSymbol = ""
Dim nbDecRnd : nbDecRnd = ""
Dim arrNb    : arrNb    = Split(nbFormat, " ")

' Asign symbol
Select Case UBound(arrNb)
    Case 0
        nbSymbol = ""   ' Hundred
    Case 1
        nbSymbol = "K"  ' Thousand
    Case 2
        nbSymbol = "M"  ' Million
    Case 3
        nbSymbol = "G"  ' Billion
    Case 4
        nbSymbol = "T"  ' Trillion
    Case 5
        nbSymbol = "P"  ' Quadrillion
    Case 6
        nbSymbol = "E"  ' Quintillion
End Select

' Concatenate rtn string
If Ubound(arrNb) > 0 Then
    If nbDigit < 3 Then
        If CInt(arrNb(1)) > 499 Then
            nbDecRnd = CStr(CInt(Left(arrNb(1),nbDigit)) + 1) ' Ceil decimal rtn
        Else
            nbDecRnd = Left(arrNb(1),nbDigit)
        End If
    Else
        nbDecRnd = Left(arrNb(1),nbDigit)
    End If
    
    rtnNb = arrNb(0) & "," & nbDecRnd & nbSymbol    ' All others
Else
    
    rtnNb = arrNb(0) & nbSymbol                     ' Hundred
End If

' Rtn value
Format_Large_Number = rtnNb

结束函数

【讨论】:

以上是关于数以百万计的字符串格式,四舍五入的数以千计的主要内容,如果未能解决你的问题,请参考以下文章

如何将双精度格式设置为四舍五入到最接近的美元的货币?

OBIEE 10G:四舍五入问题

如何在 C++ 中优雅地格式化字符串,使其四舍五入到小数点后 6 位,并修剪额外的 '0' 或 '9'

如何连接四舍五入的数字字符串python?

jquery 四舍五入截取字符串

js四舍五入不显示.00