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