.NET中的十進制浮點類型
.NET中的十進制浮點類型
首先,什么是十進制類型?
十進制類型僅是浮點數的另外一種表示形式 - 但是與單精度浮點類型和雙精度浮點類型不同,十進制類型使用的進制是10。如果你還沒有讀上面鏈接給出的文章,現在將會是讀它的最佳時間 - 我將不會在這篇文章中探討浮點類型數字。
十進制類型于任何其他浮點數字有同樣的組件: 一個尾數, 一個指數和一個符號。按照慣例, 符號位僅是一個比特,但是有96比特的尾數和5比特的指數位。然而,并不是所有指數組合都是合法的。只有值是0~28的才能工作,所有的負數也都是有效的: 數字值是符號*尾數/10指數。這意味著這個類型的最大值和最小值是+/-(296-1), 最小的非零數字在絕對值上是10-28.
指數被限制的原因是尾數可以存儲28或29個十進制數字(取決于它自己的精度)。你可以將28個數字可以設置成任何你想要的值,你可以將十進制浮點放在第一個數字的左邊到最后一個數字的右邊間的任何一個地方,(有一些你可以讓第29個數字在其余數字左邊的數字,但是由于限制,你不可以使用29個數字的所有組合。)它們都是有效的。
一個十進制數是如何存儲的
一個十進制數用128比特存儲,盡管只有102比特是嚴格必須要有的。把十進制數認為是由尾數表示的3個32位整型數是很方便的,然后就可以用一個整數表示符號位和指數位。最后一個整數的最高位是符號位(在正式方式中,將最高位設置成(1)表示負數)同時16~23位(高16位字的低位)表示指數。其他位必須都是(0). 這個表示是由decimal.GetBits(decimal)提供的可以返回一個4個整型數數組的方式。
格式化十進制數
與單精度浮點數和雙精度浮點數不同,當.NET被要求將一個十進制數格式化成一個字符串表示形式時,它的默認行為是給出精確值。這意味著二進制浮點類型文章之中的DoubleConverter代碼 里提到的一個十進制等效是沒有必要的。當然,你可以用它來將值限制到一個特殊的精度。
保留0
在.NET 1.0 和1.1 間, 十進制類型經歷了一個微妙的變化??紤]下面的簡單代碼:
- view source
- print?
- using System;
- public class Test
- {
- static void Main()
- {
- decimal d = 1.00m;
- Console.WriteLine (d);
- }
- }
當我首先運行上面的代碼時(或者一些類似的),我期待它輸出的結果是1(這是.NET 1.0中應該有的結果)——但實際上,輸出是1.00。十進制類型沒有使用它自己的標準 - 它記住了它有多少個十進制數字(通過維護可能的說明)并格式化,0可能被計入一個重要的十進制數字。當兩個不同的十進制數相乘,相除,相加等等時,我不知道選擇哪個說明(這里有一個選擇)的精確規(guī)則,但是當你用如下的程序測試時你可能會發(fā)現它很有趣。
- view source
- print?
- using System;
- public class Test
- {
- static void Main()
- {
- decimal d = 0.000 000 000 00010000m;
- while (d != 0m)
- {
- Console.WriteLine (d);
- dd = d/5m;
- }
- }
- }
它生成了一系列結果:
- view source
- print?
- 0.00000000000010000
- 0.00000000000002000
- 0.00000000000000400
- 0.00000000000000080
- 0.00000000000000016
- 0.000000000000000032
- 0.0000000000000000064
- 0.00000000000000000128
- 0.000000000000000000256
- 0.0000000000000000000512
- 0.00000000000000000001024
- 0.000000000000000000002048
- 0.0000000000000000000004096
- 0.00000000000000000000008192
- 0.000000000000000000000016384
- 0.0000000000000000000000032768
- 0.0000000000000000000000006554
- 0.0000000000000000000000001311
- 0.0000000000000000000000000262
- 0.0000000000000000000000000052
- 0.000000000000000000000000001
- 0.0000000000000000000000000002
所有的都是一個數字
十進制類型沒有無窮大或者NaN(not-a-number, 不是一個數字)值,雖然上面例子里的同樣的真實數字隱含是不同格式(比如1, 1.0, 1.00),正常的==操作符考慮了這些并報告1.0==1.00.
精確性
在.NET中十進制類型比任何內建的浮點類型有更大的精度,盡管它有一個相對比較小的默認指數范圍。很多操作使用二進制浮點類型而沒有使用十進制浮點類型來不精確的表示原始操作數卻獲得了讓人感覺驚訝的結果,精度是因為很多操作在源碼中按照十進制表示的。然而,那并不意味著所有操作突然變得精確起來: 1/3仍然是不完全表示的,例如,潛在的問題與二進制浮點的問題一樣。然而,大多數時候十進制類型用來計算總量,比如錢,操作很簡單同時保持結果精確。(例如,添加一個由百分比表示的稅負將會讓數字精確,假設它們要在一個可以判斷的范圍內開始。)僅需要注意哪些操作可能引起不精確,哪些操作不會。
作為一個非常廣泛的拇指(譯注: thumb 翻譯為拇指可能不準確,以下類同)規(guī)則,如果你結束查看一個非常長的字符串表示行為(比如大多數28/29位數字都是非零的)那么有可能你會在這個過程中得到一些不精確: 大多數對十進制類型的使用不會再數字已經精確的情況下仍然使用很多重要的數字來結束。
結論
大多數商業(yè)應用應該可能使用十進制而不是單精度浮點或者雙精度浮點。我的拇指規(guī)則是由人類制作的值比如貨幣通常用十進制浮點類型表示比較好: 例如,‘精確的1.25美元’這個的概念完全是有理由的。對來自自然世界的值,比如長度和重量,二進制浮點類型會更加有意義。盡管有一個理論的”精確 1.25米”但它不會真的在現實世界中發(fā)生: 你當然不會去精確的測量長度,而且它們也不像是能在原子層次中存在。我們通常有一個可以忍耐的范圍。
使用十進制浮點算法需要花費一個代價,但是我認為這對大多數開發(fā)人員來說不是瓶頸??傊?,首先寫最合適(可讀)的代碼,一直分析你的代碼性能。通常慢一點獲得正確答案比很快卻得到錯誤的答案更好。
【編輯推薦】