消除Decimal型別尾數多餘的補零
Decimal型別用來處理數值好處多多,但是有一個有可能不太適用於真實社會上的處理特性,那就是當你對decimal進行ToString()後,輸出會幫忙補足該數值的浮點位數,這點有時候還蠻惱人的。(會計酸:謝謝你幫我補那堆無意義的零喔!)
Decimal型別遇到浮點型態尾數會自動補零的特性
假設我們有一個decimal型別的程式碼這樣寫:
var fTest = 1.000001m;
Console.WriteLine(fTest);
fTest += 0.999999m;
Console.WriteLine(fTest);
則輸出會變成:
1.000001
2.000000
會形成這樣的原因,是因為decimal fTest
會自始至終想要保持它的精度,但是有時候我們並不會想要2.000000
這樣的結果,反而會是想要2
來表示就好。
再舉例如下方較為完整的程式碼,我們將每一次的decimal運算都加入到LIST之中:
static void Main(string[] args)
{
var oResult = new System.Collections.Generic.List<object>();
decimal fCount = 1;
for (int i = 0; i < 10; i++)
{
fCount += 0.01m;
oResult.Add(new { fPrice = fCount });
}
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(oResult, Newtonsoft.Json.Formatting.Indented));
Console.Read();
}
輸出與我們要關注的問題發生點如下:
[
{
"fPrice": 1.01
},
{
"fPrice": 1.02
},
{
"fPrice": 1.03
},
{
"fPrice": 1.04
},
{
"fPrice": 1.05
},
{
"fPrice": 1.06
},
{
"fPrice": 1.07
},
{
"fPrice": 1.08
},
{
"fPrice": 1.09
},
{
"fPrice": 1.10 ←我們想要變成1.1就好
}
]
解決方式
最快的解決方式很奇葩,因為decimal的精密度是28-29位數
,所以就是把答案除以1後面帶大於或等於28個零
就好了。以上面的例子可以將程式碼修正如下:
static void Main(string[] args)
{
var oResult = new System.Collections.Generic.List<object>();
decimal fCount = 1;
for (int i = 0; i < 10; i++)
{
fCount += 0.01m;
oResult.Add(new { fPrice = fCount / 1.0000000000000000000000000000m });
}
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(oResult, Newtonsoft.Json.Formatting.Indented));
Console.Read();
}
輸出的結果就修正成為我們想要的樣子了:
[
{
"fPrice": 1.01
},
{
"fPrice": 1.02
},
{
"fPrice": 1.03
},
{
"fPrice": 1.04
},
{
"fPrice": 1.05
},
{
"fPrice": 1.06
},
{
"fPrice": 1.07
},
{
"fPrice": 1.08
},
{
"fPrice": 1.09
},
{
"fPrice": 1.1 ←變成我們想要的樣子了,尾數的零消失了
}
]
補充說明
透過字串的尋找來解決尾數補零也是一個方法,但程式碼會稍微囉嗦一點。另外其他試圖採用parse或是透過StringFormat之類的"0.###"轉換,都請銘記一件事情,你正在試圖轉型,所以請把進位取捨考慮進去,維持decimal的精確度
讓這問題沒有那麼簡單。