為什么計算機編程語言中:0.1+0.2不等於0.3


最近在學習JS過程中發現了一個非常有意思事,就是運算0.1+0.2的結果不是0.3,而是0.30000000000000004,但先將小數做乘法然后相加,再除回來就得到想要的0.3

 

 我用python試了一下,發現python也是一樣的,結果也是0.30000000000000004。

 然后我開始信息搜集,最后找到了答案。想知道這其中的原因,要先理解這些點:二進制、指數形式、IEEE 754標准。

 1、二進制

在計算機中所有的數據都是二進制形式存儲的,包括整數、浮點數以及其他所有類型的數據。我們將十進制的0.1以及0.2轉換成二進制

轉換的方法也比較簡單,整數轉換成二進制:就是用整數除以2然后從下往上取余數,下圖用100舉例

 

 然后小數轉換成二進制就是,小數部分無限乘以2,然后順序取整。下圖用0.375舉例

 

 

 

 2、指數形式

用指數方式表示可以在有限的空間里存儲更大的數值。

 

所有的十進制數都可以用指數形式表示,成為D=M*10E,比如100可以表示為1*102

二進制數也可以用指數形式表示,B=M*2E的形式,這里的E為指數,M為B的位數。比如0.011可以表示為1.1*10-10,這里的“-10”表示的是-2。

 

3、浮點數IEEE754標准

IEEE 754規定,對於規定了四種表示浮點數值的方式:單精確度(32位)、雙精確度(64位)、延伸單精確度(43比特以上,很少使用)與延伸雙精確度(79比特以上,通常以80位實現)      //后兩個今天不作研究

 

所有的二進制數V都可以用{S,E,M}表示,也就是V=(-1)S×1.M×2E

符號位 S(Sign)能夠決定二進制數是正數,(-1)S所以當S為1時結果就是負數,S為0時結果就是正數。(而數值0的符號位會比較特殊)

有效數字位M(Significand)是二進制小數,它的取值范圍是1≤1.M<2,大於等於1小於2也就是1.XXXXXXXXX......(單精度是23位,雙精度是52位),由於有效數字位的取值整數位永遠是1,所以可以被舍去,保留后面的XXXXXXX.....部分,這樣就節省了一位有效數字。

指數位 E(Exponent)是 2 的冪(單精度是8位,雙精度是52位)是一個無符號整數,就是說它一定是正整數。比如現在E的長度為8位,E的取值范圍就是0——255(因為最小就是八個0,最大就是八個1);長度為11位,E的取值范圍就是0——2047。但是由於指數位是可以存在負數的,所以為了表達出指數的負數形式設計出了偏移量(單精度137,雙精度1023),用E真實的數值加上偏移量計算出指數位(也可以理解為1023表示的是0,小於1023就是負數,大於1023就是正數)。

 

下面以0.375舉例

 

 對於指數的值全部為0或者1的情況IEEE 754有特殊規定:

當指數全部為1,有效數字全部為0時,表示的就是正0和負0:

+0:0 00000000000 0000000000......一共64個0

-0:1  00000000000 0000000000......一共63個0

但它其實並不是精確的0,只是最接近0的數值,因為它的值是

V=(-1)^S×1.M×2^E  :(-1)0*1.00000000000......*2-1023,有效數字為這里永遠默認有個1

 

當指數全部為1時,如果有效數字M全為0,表示±無窮大;如果有效數字M不全為0,表示這個數不是一個數(NaN)。

V=(-1)^S×1.M×2^E  :(-1)0*1.00000000000......*21023                        //正負取決於S

 

 

 

 

 

 JavaScript中所有數字包括整數和小數都只有一種類型,是遵循 IEEE 754 標准,使用64位固定長度來表示,也就是標准的 double 雙精度浮點數。

一、首先將十進制的0.1轉換成二進制:0.0011001100110011......

 

 

 

然后將十進制的0.2轉換成二進制:0.0011001100110011........

 

 

 

二、將二進制的0.1和0.2轉換成指數形式

十進制:0.1

二進制形式:0.000110011001100110011......

指數形式:1.10011001100110011......*2-100          //指數是-4

 

十進制:0.2

二進制形式:0.001100110011001100110011......

指數形式:1.100110011001100110011......*2-11     //指數是-3

 

三、將指數形式轉換為IEEE754標准

這里由於IEEE754標准規定了尾數只能保留52位,所以無限循環小數需要做四舍五入,只保留52位小數(就是第53位以后全部舍棄,第53位“0舍1入”,而當第52位為1時,還得再進一位)

 

 

 將十進制0.2轉換成IEEE 754標准:

 

 

 

 

 

 

 

四、使用IEEE 754形式的 0.1+0.2    (簡單寫了)

 

 

簡單寫的話應該就是10.0100100100100100100100100100100......100111,但是此時得到的數指數依然是指數形式,要將它轉換成為二進制形式,就是將小數點向前移三位變成0.01001001001001001001001.....00111

然后將這個數轉換為十進制數就是0*2-1+1*2-2+0*2-3+0*2-4.......+1*2-52之后就可以得到0.30000000000000004了

 

總結一下就是:

計算機存儲數據時由於空間有限,所以只能存儲固定長度的數據,而像一些無限循環的數據只能被四舍五入,所以一些數據可能會和它本身有一些誤差,所以做相加運算也會有誤差。

 那么最后再看一下(0.1*10+0.2*10)/10為什么等於3,由於0.1和0.2先做乘法運算所以就變成了整數形式,所以有效數字為全部變成了0,避免了計算時的誤差,所以就的到了想要的0.3。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM