2015-07-22 84 views
3

根據以下規則,將十進制值轉換爲字符串的優雅方法是什麼?使用非常規規則格式化十進制值

  1. 顯示在小數點前面的所有數字。
  2. 顯示一個逗號,而不是小數點。
  3. 如果小數點後的部分爲非零值,只顯示顯著位,但用最小的2.

實例:

decimal  string 
------------ ---------- 
500000  500000, 
500000.9  500000,90 
500000.90  500000,90 
500000.900 500000,90 
500000.9000 500000,90 
500000.99  500000,99 
500000.999 500000,999 
500000.9999 500000,9999 

我可以很容易地小數點前顯示部和逗號,通過將值轉換爲int。但是,對於小數點後的部分處理不同的情況會變得冗長乏味。

如果有指定的方式,我想只有小數點後,但沒有小數點,我手上有這個。類似於String.Format("{0:.00#}", value),只顯示小數點。

回答

1

這裏有一個簡潔和簡單的解決方案(.NET Fiddle):

public static string FormatDecimal(decimal d) 
{ 
    string s = d.ToString("0.00##", NumberFormatInfo.InvariantInfo).Replace(".", ","); 
    if (s.EndsWith(",00", StringComparison.Ordinal)) 
     s = s.Substring(0, s.Length - 2); // chop off the "00" after integral values 
    return s; 
} 

如果你的價值觀可能有超過四個小數位,然後根據需要添加額外的#字符。格式字符串0.00##########################具有28個小數位數,可以容納所有可能的decimal值。

+0

對InvariantInfo和StringComparison.Ordinal的使用非常感謝。 – ErikE

+0

P.S.你的答案幫我創建了這個函數:'private static int GetNumberOfUsedDecimalPlaces(十進制值){返回值(值 - (int)值).ToString(「0。################ ############「,NumberFormatInfo.InvariantInfo).Replace(」。「,」「).Length - 1; }' – ErikE

2

我不會稱這個漂亮,但它屬於「它有效」的範疇。

首先實施,

public static class FormatProviderExtensions 
{ 
    public static IFormatProvider GetCustomFormatter(this NumberFormatInfo info, decimal d) 
    { 
     var truncated = Decimal.Truncate(d); 

     if (truncated == d) 
     { 
      return new NumberFormatInfo 
      { 
       NumberDecimalDigits = 0, 
       NumberDecimalSeparator = info.NumberDecimalSeparator, 
       NumberGroupSeparator = info.NumberGroupSeparator 
      }; 
     } 

     // The 4th element contains the exponent of 10 used by decimal's 
     // representation - for more information see 
     // https://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx 
     var fractionalDigitsCount = BitConverter.GetBytes(Decimal.GetBits(d)[3])[2]; 
     return fractionalDigitsCount <= 2 
      ? new NumberFormatInfo 
      { 
       NumberDecimalDigits = 2, 
       NumberDecimalSeparator = info.NumberDecimalSeparator, 
       NumberGroupSeparator = info.NumberGroupSeparator 
      } 
      : new NumberFormatInfo 
      { 
       NumberDecimalDigits = fractionalDigitsCount, 
       NumberDecimalSeparator = info.NumberDecimalSeparator, 
       NumberGroupSeparator = info.NumberGroupSeparator 
     }; 
    } 
} 

和用法示例:

var d = new[] { 500000m, 500000.9m, 500000.99m, 500000.999m, 500000.9999m }; 
var info = new NumberFormatInfo { NumberDecimalSeparator = ",", NumberGroupSeparator = "" }; 

d.ToList().ForEach(x => 
{ 
    Console.WriteLine(String.Format(info.GetCustomFormatter(x), "{0:N}", x)); 
}); 

輸出:

500000 
500000,90 
500000,99 
500000,999 
500000,9999 

它吸引我們關心從現有NumberFormatInfo的屬性,並返回一個新的一個與我們想要的NumberDecimalDigits。在醜陋的規模上它相當高,但使用足夠簡單。

+1

這看起來對於生產代碼非常可怕:'BitConverter.GetBytes(Decimal.GetBits(d)[3])[2]'。我明白你在做什麼,但對一些開發者來說這將是不透明的。嗯... – ErikE

+0

*這是相當高的醜陋的規模* - ;;) – jdphenix

+0

請注意,對於'500000.9000m'的輸入,此代碼產生'「500000,9000」'。最後兩個額外的零可能會或可能不需要。 –

0

不知道你想怎麼優雅的這是,但這裏實現你問的直接的方式。

List<decimal> decimals = new List<decimal> 
{ 
    500000M, 
    500000.9M, 
    500000.99M, 
    500000.999M, 
    500000.9999M, 
    500000.9000M 
}; 

foreach (decimal d in decimals) 
{ 
    string dStr = d.ToString(); 
    if (!dStr.Contains(".")) 
    { 
     Console.WriteLine(d + ","); 
    } 
    else 
    { 
     // Trim any trailing zeroes after the decimal point 
     dStr = dStr.TrimEnd('0'); 

     string[] pieces = dStr.Split('.'); 
     if (pieces[1].Length < 2) 
     { 
      // Ensure 2 significant digits 
      pieces[1] = pieces[1].PadRight(2, '0'); 
     } 

     Console.WriteLine(String.Join(",", pieces)); 
    } 
} 

結果:

500000, 
500000,90 
500000,99 
500000,999 
500000,9999 
500000,90 
+0

請注意,對於輸入「500000.9000m」,此代碼產生'「500000,9000」'。最後兩個額外的零可能會或可能不需要。 –

+0

@MichaelLiu感謝您的發現!回答更新 – Shar1er80

+1

在小數點上使用簡單的'ToString()'可能會產生不同文化的奇怪結果,是的?如果默認文化是對小數點分隔符使用逗號的文化,那麼這是否正確? – ErikE