|
大多數人會對他們在.NET中的算術的"出錯"首先感到驚訝。使用一些稱為”浮點”算術來表示非整型數字不是.NET 相比其他大多數語言/平臺特殊的地方。在.NET 內部是沒問題的,但是你需要知道一些底層正在發生什么,否則你將會對一些結果感到驚訝。
我在這個事情上不是一個專家這不重要。雖然寫了這篇文章,我也發現了另外一篇 - 這次是一個真正的專家寫的,杰弗里 薩克斯(Jeffrey Sax)。我強烈建議你也同時讀他的浮點文章。
什么是浮點數?
計算機總是需要一些表示數據的方式,最終這些表示數據的方式總是歸結為二進制(0,1組合)。整數很容易表示(對負數有合適的轉換,有確定好的范圍可以知道表示從多大開始)但是非整數有一些復雜。不管你想出什么方法,總是有一個問題。例如,使用我們自己的十進制方式寫數字: 仍然(在十進制內部)不能表達三分之一,你只是在一個3循環中結束。無論你使用多少進制,一些數字都會產生同樣的問題 - 特別的,“無理數”的數字(那些不能用以分數表示的數字)如常量PI(音: pai)和e(指數e)總是有一些問題。
你可以將所有有理數用精確的兩個整型數表示,第一個數被第二個數除的結果 - 但是即便是一個非常”簡單”的操作整數都可以增長的非常大且非常快,平方根操作也會趨向產生無理數。有很多其他的因素會導致導致,但是最常用的解決問題的方式就是使用一種格式或其他格式的浮點類型。思想就是基礎有可以用來擴展表達的一些數字(尾數),另外(指數)用來表示規模是多大,以“小數點要去哪里”的形式表示。例如,34.5可以用”十進制浮點類型”3.45加上一個指數1來表示,同樣的3450也可以有同樣的尾數和一個指數3(34.5是3.45x101,3450是 3.45x103)來表示。現在,為了簡單起見例子使用十進制表示,但是大多數浮點類型是二進制表示的。例如,二進制尾數1.1加上尾數-1將意味著十進制0.75(二進制1.1==十進制1.5,在二進制中指數-1意味著”被2除”,十進制同樣的指數-1表示”被10除”,二進制1.1==20.2-1==1.5(譯者注)).
理解在同樣的方式你不能通過一個十進制擴充(無限)來精確表達三分之一是很重要的,有很多數字在十進制形式看起來很簡單,但是在二進制表示中卻有長的或者無限的擴展。這意味著(舉例)一個二進制浮點變量不能有精確的十進制值0.1。相反,假設你又一些如下代碼:
double x = 0.1d;
變量x實際上將存儲最接近那個值的double型值。一旦你腦子里可以轉過彎兒,那么為什么一起計算結果看起來是”錯誤”的將會變得很明顯。如果你被要求計算1/3 + 1/3,這兩個數相加的結果是0.666,而不是0.667(更接近兩個1/3 的和)。一個二進制浮點類型的表達式是3.65d+0.05d != 3.7d(盡管在一些情況下它顯示成3.7)。
.NET 中的浮點類型是什么樣子的?
C#標準僅列出double和float作為可用的浮點類型(這些是C#中System.Double和System.Single的速記表示),但是decimal類型(速記表示為System.Decimal)實際上也是一個浮點類型 - 它僅是十進制浮點類型,但是指數的范圍很有趣。decimal類型在另外一篇文章中描述,所以這篇文章不會做任何深入探討 - 我們關注double和float.這兩個都是二進制浮點類型,參照IEEE 754(一個多種浮點類型的標準定義)。float是一個32位類型(1個符號位, 23位的尾數和8位指數), double是一個64位類型(1個符號位, 52位尾數和11位指數)。
結果不是我期望的是不好的結果嗎?
好吧,那取決于情況。如果你在寫財務軟件,你可能要非常嚴格的定義處理錯誤的方式,數量也是直覺上用10進制表示 - 在這種情況decimal類型更加與float或者double類型相似。如果,然而,如果你在寫一個科學應用程序,使用十進制浮點表示法可能會有一點弱,你也可能想要開始處理一些低精度的數目(一美元就是一美元,但是如果你在測量一個單位是米的長度,你可能開始有一些不精確。)
比較浮點數字
所有這些可以得出一個推論,你應該非常,非常少的去直接比較浮點數間是否相等。通常比較大于或者小于會好些,但是當你對相等感興趣時你應該總是考慮是否你實際上想要的接近相等:一個數字總是與另外一個相同。做這個的一個簡單的方式是用一個數減去另外一個數,使用Math.Abs來找到絕對值的不同,然后檢查是否這個誤差是否低到可以忍受的級別。
也有一些情況是病理的,這些是由于JIT優化導致。查看下面的代碼:
using System;class Test{ static float f; static void Main(string[] args) { f = Sum (0.1f, 0.2f); float g = Sum (0.1f, 0.2f); Console.WriteLine (f==g);
//g = g + 1;
} static float Sum (float f1, float f2) { return f1+f2; }}
NET技術:.NET 中的二進制浮點類型,轉載需保留來源!
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。