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