|
浮點數(shù)在計算機中的表示
最后編輯于:2010-4-13
計算機中數(shù)字是以0和1二進制保存的,我們熟悉的是整數(shù)的如何在計算機中表示,那么浮點數(shù)是如何表示的呢?
一. 轉(zhuǎn)換
我們先來看看如何將十進制的浮點數(shù)轉(zhuǎn)換成二進制。
一個十進制的浮點數(shù),例如:abcd.efg (其中a~g為0..9),其值用多項式為:
a*10^3 + b*10^2 + c*10^1+d*10^0+e*10^(-1)+f*10^(-1)+g*10^(-3)。
而一個二進制的浮點數(shù),我們也將其表示成:abcd.efg (其中a~g為0或1),其值表示為:
a*2^3 + b*2^2 + c*2^1+d*2^0+e*2^(-1)+f*2^(-1)+g*2^(-3)。
我們看到底由十進制時的10換成了二進制時的2了,其它都一樣。所以一個十進制的浮點數(shù)轉(zhuǎn)換成二進制必須分兩步進行:整數(shù)部分和小數(shù)部分。
1. 對于整數(shù)部分,和以前的整數(shù)轉(zhuǎn)換是一樣的。
2. 對于小數(shù)部分,比較特殊。下面講兩種轉(zhuǎn)換方法。
方法一:依次與2^(-n)作比較(n從1開始),若大于該值則為1,且減去此值,否則為0;然后繼續(xù)下一輪比較。舉例說明:將0.842356轉(zhuǎn)換成二進制:
此時,你會發(fā)現(xiàn)比較將會是無窮無盡的。如果你截取到某位,必須做一些取舍。取舍的標準是:其后一位若為1則進1;后一位為0則不進。
還是以上面為例,若要截取9位,因為第10位為0,故不進位,則最終的結(jié)果為:0.110101111
;若要截取到8位,因為第9位為1,故要進位,則最終的結(jié)果為:0.110110000
(即0.1101101111 + 0.0000000001)。從這個例子可以看出十進制小數(shù)的轉(zhuǎn)換成二進制時只是一個近似值。其實大部分浮點數(shù)保存在計算機中都只是一個近似值。至于是稍微大于原值還是稍微小于原值,要看截取時有無進位。
方法二:若在計算機中計算方法一的過程,因為2^(-n)本身就是一個浮點數(shù),而浮點數(shù)之間的比較和計算難免有誤差。所以我想到了下面這個方法:
1) 首先生成首數(shù)字為1、后面0的個數(shù)為小數(shù)位數(shù)的基準數(shù),比如0.254的基準數(shù)為1000、0.00353的基準數(shù)為100000。
2) 將小數(shù)部分乘以上面的基準數(shù),這樣得到一個整數(shù)。
3) 對該整數(shù)乘以2,若積大于基準數(shù),則為1,同時將積減去基準數(shù)后得新的整數(shù);若積小于基準數(shù),則為0。
4) 用新的整數(shù)重復(fù)步驟3,直到整數(shù)為0或者到需要的精確位數(shù),作取舍后結(jié)束。
舉例說明:將0.842356轉(zhuǎn)換成二進制,基準數(shù)為1000000,轉(zhuǎn)換成整數(shù)為842356,
此法可以有效地避免浮點數(shù)的比較,能方便且快捷地獲得對應(yīng)的二進制值。
二. 存儲
現(xiàn)在已轉(zhuǎn)換成浮點二進制了,那么如何在計算機中表示呢?這要說到科學(xué)計數(shù)法,這個大家比較熟悉。十進制的科學(xué)計算法可以表示成如下:(-1)^s * M * 10^E
,S表示符號:S為1表示負數(shù);0為正數(shù)。M成為尾數(shù),其范圍為1<=M <10 。E被成為冪,也叫指數(shù)。
二進制的科學(xué)計數(shù)法,也是IEEE的浮點數(shù)標準格式,和十進制格式一樣:(-1)^s * M * 2^E
。M的范圍1<= M <2。將一個二進制浮點數(shù)轉(zhuǎn)換成科學(xué)計算法很簡單,例如:
1)10001.110001 小數(shù)點左移4位后成 (-1)^0 * (1.0001110001) * 2 ^ 4.
2) -0.000010001 小數(shù)點右移4位后成(-1)^1 * (1.0001) * 2 ^ ( – 4)
在計算機中,表示浮點數(shù)由兩種常用的格式:單精度浮點數(shù)和雙精度浮點數(shù),它們在精度上有所差別,同時所需要的空間也有差別:
1) 當(dāng)為負數(shù)時,符號位為1,否則為0。
2) 指數(shù)有正數(shù)亦有負數(shù),這里保存時使用了加偏移量的方法:8位指數(shù)位的指數(shù)范圍為-127~128,其偏移量為127;11位指數(shù)范圍為-1023~1024,其偏移量為1023。保存時指數(shù)加上偏移量,可以避免負數(shù)問題;取值時再減去偏移量就行了。
3) 因為尾數(shù)1<=M<2,就是說小數(shù)點前面總是有一個1。為了節(jié)省空間,將此處的1省去,直接將小數(shù)點后面的部分放入到小數(shù)部分(這也是這部分為什么叫“小數(shù)部分”,而不是“尾數(shù)部分”的原因)。
舉一例:將12.842356保存成單精度浮點格式。
1) 首先將它轉(zhuǎn)換成二進制格式:1100.11010111101001001010,后面位直接截去。
2) 轉(zhuǎn)換成科學(xué)計數(shù)法格式:1. 10011010111101001001010。右移3位于是指數(shù)為3+127=130,二進制為10000010。
3) 于是符號位為0,指數(shù)為:10000010,小數(shù)去掉前面的1后為10011010111101001001010。這些二進制就是最終保存在電腦里的格式:0 10000010 10011010111101001001010,十六進制格式為:0x414D7A4A。
三. SQL SERVER中的浮點數(shù)
SQL SERVER浮點數(shù)有float和real兩種。Float類型定義格式:float [ ( n ) ]。 n 為用于存儲科學(xué)記數(shù)法 float 數(shù)尾數(shù)的位數(shù),同時指示其精度和存儲大小。n 必須為從 1 到 53 之間的值。
精度與存儲字節(jié)之間的關(guān)系:
在 SQL Server 中,real 的同義詞為 float(24)。
比起其他的精確數(shù)值類型,浮點數(shù)的優(yōu)點是保存的數(shù)值大。比如單精度由于其指數(shù)為8位,故其最大值約為1.1 * 2^128 = 3.4 * 10^38;雙精度其指數(shù)為11位,故其最大值約為1.1 * 2^1024 = 1.79 * 10^308。
當(dāng)然,浮點數(shù)的最大缺點就是數(shù)據(jù)的近似值保存。就是這個原因,導(dǎo)致了下面的一個小小的問題。
四. SQL SERVER中浮點數(shù)的一個問題
因為float和real格式只能保存近似值,除非要保存特別大的數(shù)值,否則推薦使用精確數(shù)值類型。下面說說在SQL SERVER中遇到的一個浮點問題,其實這也是為什么寫這篇文章的原因。最近有人問下面這個問題:
Declare @a, @b float
Set @a = 1.465
Set @b = 2.465
Select round(@a, 2), round(@b, 2)
結(jié)果分別為1.47和2.46,這是為什么?
上文中我們說過,大部分浮點數(shù)保存在計算機中都只是一個近似值。小數(shù)部分轉(zhuǎn)化成二進制時,會根據(jù)情況截去或進位。
1.465保存到計算機中時,小數(shù)的最后一位需要進位(結(jié)果為0x3FF770A3D70A3D71),這就造成了保存的值比實際的1.465稍微大一點。0x3FF770A3D70A3D71轉(zhuǎn)換成十進制后值約為1.4650000000000001。
而2.465卻正好相反,它在保存到計算機中時,小數(shù)的最后一位后面直接截掉(結(jié)果為0x4003B851EB851EB8),這就造成了保存的值比實際的2.465稍微小一點。0x4003B851EB851EB8轉(zhuǎn)換成十進制后值約為2.4649999999999999。
Round函數(shù)只是對保存的值進行了四舍五入,沒有任何問題。若選擇使用精確數(shù)值類型,可以有效地避免這種問題的發(fā)生。
五. 參考文獻
1. http://blog.csdn.net/YUKUILONGQQ/archive/2008/10/27/3157627.aspx
2. http://blog.sina.com.cn/s/blog_4c7fa77b01000ai2.html
我寫了一個程序,模擬浮點數(shù)的內(nèi)存格式,下載地址為:http://download.csdn.net/source/2233150
|