|
0、導(dǎo)讀這篇文章主要探討了浮點數(shù)在計算機中的表示、存儲和精度問題。通過詳細(xì)的解釋和示例,您將了解浮點數(shù)誤差的根源。文章內(nèi)容較多,大約3700余字,閱讀時間約為10分鐘,建議先收藏,待有空時再細(xì)細(xì)品讀。
1、引言0.1 + 0.2 為什么不等于 0.3 ?
當(dāng)被問及浮點數(shù)為何存在誤差時,你將如何回答?
沒看完這篇文章之前你可能會回答:"哼,反正我就知道有誤差..."
閱讀完這篇文章后,你將能夠更準(zhǔn)確地回答這類問題,讓我們開始這段學(xué)習(xí)之旅吧!
2、浮點數(shù)存儲格式浮點型在內(nèi)存中的存儲不是像整形那樣直接存儲的,而是用一種二進制的科學(xué)計數(shù)法來表示的,具體的數(shù)學(xué)表達(dá)式為
V = (-1) s × M × 2 e其中,e = E - 127
在計算機科學(xué)領(lǐng)域,IEEE 754 是一種標(biāo)準(zhǔn),用于定義浮點數(shù)的表示方法,浮點型數(shù)據(jù)的存儲格式如下
2kxhas1phfb640971416.png (47.18 KB, 下載次數(shù): 0)
下載附件
保存到相冊
2kxhas1phfb640971416.png
前天 23:52 上傳
請務(wù)必記住,尾數(shù)存儲用原碼,階碼存儲用移碼
S(符號位):0代表正數(shù),1代表負(fù)數(shù)。E(階碼):指數(shù)字段需要同時表示正指數(shù)和負(fù)指數(shù)。為了得到存儲的指數(shù),在實際指數(shù)上加一個偏置,其中e=E-127。M(尾數(shù)):一個規(guī)范化尾數(shù)就是小數(shù)點左邊只有一個1,然后是小數(shù)點后面的尾數(shù)部分。注意本文后續(xù)使用的e表示科學(xué)計數(shù)法中的指數(shù)部分,E表示存儲格式中的階碼,默認(rèn)的對象都指單精度的浮點數(shù)。
3、轉(zhuǎn)換流程接下來我選擇了一個戀愛腦的數(shù)字,將1314.520轉(zhuǎn)換到32位單精度IEEE 754二進制浮點表示標(biāo)準(zhǔn)。
3.1、將整數(shù)部分轉(zhuǎn)換為二進制將整數(shù)部分反復(fù)除以2,并記錄每次的余數(shù),直到商為0為止。
division = quotient + remainder;
1314 ÷ 2 = 657 + 0;
657 ÷ 2 = 328 + 1;
328 ÷ 2 = 164 + 0;
164 ÷ 2 = 82 + 0;
82 ÷ 2 = 41 + 0;
41 ÷ 2 = 20 + 1;
20 ÷ 2 = 10 + 0;
10 ÷ 2 = 5 + 0;
5 ÷ 2 = 2 + 1;
2 ÷ 2 = 1 + 0;
1 ÷ 2 = 0 + 1;
從上面構(gòu)造的列表的底部開始取所有余數(shù),即為整數(shù)部分的二進制表示。131410=101 0010 00102
3.2、將小數(shù)部分轉(zhuǎn)為二進制將小數(shù)部分不斷乘以2,并記錄每次的整數(shù)部分,直到小數(shù)部分為0或達(dá)到所需的精度為止
#) multiplying = integer + fractional part;
1) 0.52 × 2 = 1 + 0.04;
2) 0.04 × 2 = 0 + 0.08;
3) 0.08 × 2 = 0 + 0.16;
4) 0.16 × 2 = 0 + 0.32;
5) 0.32 × 2 = 0 + 0.64;
6) 0.64 × 2 = 1 + 0.28;
7) 0.28 × 2 = 0 + 0.56;
8) 0.56 × 2 = 1 + 0.12;
9) 0.12 × 2 = 0 + 0.24;
10) 0.24 × 2 = 0 + 0.48;
11) 0.48 × 2 = 0 + 0.96;
12) 0.96 × 2 = 1 + 0.92;
13) 0.92 × 2 = 1 + 0.84;
14) 0.84 × 2 = 1 + 0.68;
15) 0.68 × 2 = 1 + 0.36;
16) 0.36 × 2 = 0 + 0.72;
17) 0.72 × 2 = 1 + 0.44;
18) 0.44 × 2 = 0 + 0.88;
19) 0.88 × 2 = 1 + 0.76;
20) 0.76 × 2 = 1 + 0.52;
21) 0.52 × 2 = 1 + 0.04;
22) 0.04 × 2 = 0 + 0.08;
23) 0.08 × 2 = 0 + 0.16;
24) 0.16 × 2 = 0 + 0.32;
雖然我們沒有得到任何等于0的小數(shù)部分,但是我們有足夠的迭代(超過尾數(shù)限制)。
從頂部開始依次取乘法運算的所有整數(shù)部分,即為小數(shù)部分的二進制:0.5210=0.1000 0101 0001 1110 1011 10002
3.3、規(guī)范化前面得出了整數(shù)以及小數(shù)部分的二進制表示,合并以后即:
1314.5210= 101 0010 0010.1000 0101 0001 1110 1011 10002
將小數(shù)點向左移動 10 位,使其左邊只剩下一位非零的數(shù)字
1314.5210= 101 0010 0010.1000 0101 0001 1110 1011 10002= 101 0010 0010.1000 0101 0001 1110 1011 10002 ×2 0= 1.0100 1000 1010 0001 0100 0111 1010 1110 002 ×2 10
再回顧一下浮點數(shù)的數(shù)學(xué)表達(dá)式 V = (-1) s × M × 2 e 由此可知
s = 0
M = 1.0100 1000 1010 0001 0100 0111 1010 1110 00
e = 10
3.4、調(diào)整階碼根據(jù)規(guī)范化得知指數(shù) e = 10,又根據(jù)公式 e = E - 127 可得知道 E=137,所以八位階碼的二進制表示如下所示:
E = 13710 = 1000 10012
3.5、尾數(shù)舍入由第三步規(guī)范化得出的尾數(shù)M有34位,但是存儲格式中尾數(shù)只有23位,下面劃線的是多出的部分,所以需要對尾數(shù)按照一定的方式進行四舍五入。
M = 1. 0100 1000 1010 0001 0100 011 1 1010 1110 00
一共有四種舍入方式,
向偶數(shù)舍入,就近舍入(默認(rèn))。朝0舍入:即朝數(shù)軸零點方向舍入,即直接截尾。朝正無窮舍入:對正數(shù)而言,只要多余位不全為0則向最低有效位進1;負(fù)數(shù)則直接截尾。朝負(fù)無窮舍入:對負(fù)數(shù)而言,向最低有效位進1;正數(shù)若多余位不全部為0則簡單截尾。向偶數(shù)舍入,簡單理解就要讓尾數(shù)的最后一位為0,讓其保持偶數(shù),能夠被2整除。當(dāng)尾數(shù)的最低位為0時,已經(jīng)是屬于偶數(shù)了,無需處理。當(dāng)尾數(shù)最低位為1時,需要加1,使其保持偶數(shù)。
因為本例計算出尾數(shù)的最后一位為1,按照就近舍入(向偶舍入)原則需要加1使其保持偶數(shù)。
所以經(jīng)過調(diào)整后的M為
M = 0100 1000 1010 0001 0100 011 + 1
M = 0100 1000 1010 0001 0100 100
3.6、組三元素根據(jù)前面的步驟可以得知
s = 0
E = 1000 1001 2
M = 0100 1000 1010 0001 0100 100 2
1324.5210 = 0-1000 1001-0100 1000 1010 0001 0100 1002
我們?nèi)ヒ粋轉(zhuǎn)換網(wǎng)站上驗證一下轉(zhuǎn)換結(jié)果,網(wǎng)站鏈接放在文章末尾了。
qbpigsntumj640971516.png (41.05 KB, 下載次數(shù): 0)
下載附件
保存到相冊
qbpigsntumj640971516.png
前天 23:52 上傳
floatConverterIEEE754可以看到,跟我們轉(zhuǎn)換的結(jié)果是相同的,說明網(wǎng)站轉(zhuǎn)換也是選擇向偶數(shù)舍入的。
4、單/雙精度浮點數(shù)比較4.1、存儲格式類型符號位指數(shù)長度(Bit)尾數(shù)長度(Bit)float1823double111524.2、精度浮點數(shù)的精度是由尾數(shù)的位數(shù)來決定的。
對于float型浮點數(shù),尾數(shù)部分23位,換算成十進制就是 2^23=8388608,所以十進制精度只有6 ~ 7位;
這里的數(shù)字6和7可能會引起疑問,如何理解它們呢?
由于浮點數(shù)尾數(shù)的舍入問題,最后一位可能存在舍入誤差,因此不完全準(zhǔn)確。因此,可以準(zhǔn)確表示的是后六位,而第七位則可能含有誤差。
對于double型浮點數(shù),尾數(shù)部分52位,換算成十進制就是 2^52 = 4503599627370496,所以十進制精度只有15 ~ 16位
類型有效位字節(jié)數(shù)float6 - 74double15 - 1684.3、浮點數(shù)范圍類型最小值最大值float1.175494351 E - 383.402823466 E + 38double2.2250738585072014 E - 3081.7976931348623158 E + 3084.4、浮點數(shù)比較浮點數(shù)的比較通常用兩數(shù)之差的絕對值小于一個自定義的數(shù)值時,代表兩者相等,如下所示:
/**
*Author:(公眾號:typedef)
*/
#define FLOAT_EPSILON (0.000001) //Define your own tolerance
#define FloatIsEqual(a, b) ((fabs((a)-(b)))
另外一種方法是將浮點數(shù)同時放大一個倍數(shù),然后轉(zhuǎn)成整數(shù)之間的比較,比如同時放大10000倍等。
5、階碼相關(guān)問題探索首先階碼E是用移碼表示的,那么問題來了,什么叫移碼?移碼怎么計算?移碼的含義是?浮點數(shù)為什么要用移碼表示?
在解答這些知識點時,我們需要下面兩點需要達(dá)成一致
階碼使用的是非標(biāo)準(zhǔn)移碼階碼是一個無符號的整數(shù)5.1、什么是移碼移碼是補碼表示中最高符號位取反的結(jié)果。舉個例子,上面計算1314.52時,指數(shù)是為10的。
+1010 = 0000 10102(真值)
原碼:0000 1010
反碼:0000 1010
補碼:0000 1010
移碼:1000 1010
所以10對應(yīng)標(biāo)準(zhǔn)的移碼 1000 1010 。
5.2、如何計算移碼注意浮點數(shù)中移碼的計算是非標(biāo)準(zhǔn)的,僅偏移2n-1-1=127。所以移碼的計算公式如下所示,其中n為階碼的位數(shù):
E = e + 2 n-1 - 1
E = e + 127
所以10對應(yīng)的移碼為137。
5.3、為什么要用移碼表示它通過將數(shù)值加上一個固定的偏移量,使得原本可能是負(fù)數(shù)的數(shù)值變?yōu)榉秦?fù)數(shù),從而簡化了計算機中有符號數(shù)的表示和比較操作。使得計算機能夠直接使用整數(shù)運算來比較浮點數(shù)的大小。
6、指數(shù)e6.1、指數(shù)范圍浮點數(shù)指數(shù)部分的實際取值范圍是 [-2(e-1)+2, 2(e-1)-1],其中 e 為指數(shù)所占位數(shù)。32位浮點數(shù),指數(shù)占8位,實際取值范圍是 [-126, 127]。
-127用作表示0,128 用作表示無窮大和 NaN。NaN 是 "Not a Number" 的縮寫,中文意思是“非數(shù)字”,通常用于表示一個未定義或不可表示的值。
換言之,8位階碼的表示范圍是[0, 255],其中0和255用于表示特殊值。因此,根據(jù)公式推導(dǎo),指數(shù)e的實際取值范圍是[-126, 127]。
6.2、特殊值形式指數(shù)(e)階碼(E)小數(shù)部分零-12700無窮1282e-1 = 2550NaN(非數(shù))1282e-1 = 255非07、文中問題解答此時再來回答文中引言提出的問題, 0.1 + 0.2 為什么不等于 0.3 ?
/**
* Author:(公眾號:typedef)
*/
#include
int main() {
double a = 0.1 + 0.2;
printf("%.17f", a);
}
輸出為0.30000000000000004,由于在尾數(shù)舍入時會帶來一定的誤差,所以并不完全相等。
當(dāng)在被問及浮點數(shù)為何存在誤差時,你將如何回答?歡迎文章留言說出你的看法。
如果不從技術(shù)的角度回答這個問題,可以這樣回答:整數(shù)是離散的,有限的并能夠被計算機表示的,小數(shù)部分是連續(xù)的,包含無窮多的數(shù),數(shù)量之多是無法被計算機存儲的,只能存儲計算機能夠表示的最接近這個數(shù)值的小數(shù)部分,所以可能會不相等。
8、參考鏈接https://www.cnblogs.com/gyunf/p/12816817.htmlhttps://www.h-schmidt.net/FloatConverter/IEEE754.htmlhttps://zh.wikipedia.org/wiki/IEEE_754https://docs.pingcode.com/ask/304021.html9、總結(jié)本篇文章深入分析了浮點數(shù)的存儲格式到轉(zhuǎn)換流程,再到指數(shù)e以及階碼E的探索,大家應(yīng)該對浮點數(shù)有了更全面的理解。
猜你喜歡:
一個非常輕量的嵌入式日志庫!
一個非常輕量的嵌入式線程池庫!
Github上熱門 C 語言項目匯總!
實用 | 10分鐘教你通過網(wǎng)頁點燈
WiFi6+藍(lán)牙+星閃,三合一開發(fā)板,真香! |
|