1. 程式人生 > >javascript浮點數學習總結之0.1+0.2

javascript浮點數學習總結之0.1+0.2

掘金搬來思否

浮點數的儲存格式:IEEE754-64bit

64位組成格式為:S(1位符號位) E(11位階碼)M(52位尾數)

  • 符號位:決定正負,0位正,1位負
  • 階碼:指數位則為階碼-1023,決定了數值的大小
  • 尾數:有效數字,決定了精度
  • 用科學計數法格式則為:(-1^(符號位0/1)) 1.xxxxx(尾數位) 2^(指數位)

需要記住的IEEE754規範有:

  • 尾數位:有效數字的第一位必定是1,所以這個1不會被儲存,也就是說實際上尾數位52+1,類似1.xxx第一位就是1。
  • 階碼:不存在負值,那怎麼表示負指數呢?就是減去一個固定值;其值就是1023,也就是偏移量1023;除去全是1和全是0的情況,那麼指數位的範圍則是[(1-1023),(2046-1023)] == [-1022,1023]。
  • 當階碼全位0,尾數全為0時,採取非規格化,兩者不包含隱含的1,用來表示0。
  • 最簡單的精度演算法就是:2^53是16位十進位制,所以15位肯定能精確處理。
  • 當然還有很多,有興趣可以自行查閱一下。

知道了這些,就能更好的理解一些數值的由來:

Number.MAX_VALUE = 1.7976931348623157e+308;
尾數:1.xxx1,後面為52個1,
要得到最大值,我們就需要把小數點往後移,就靠指數1023-52,剩餘971;  
因此最大值等價於((Math.pow(2,53)-1)*Math.pow(2,971))
Number.MIN_VALUE =5e-324;  
尾數:1.xxxx1,後面第52位為1,其餘為0,這時首位的1需要隱藏,
這時候就是負了52位,在加上2^-1022,
等價於((Math.pow(2,-52))*Math.pow(2,-1022))
Number.MAX_SAFE_INTEGER = 9007199254740991;
安全數就是能夠精確處理的,精度靠尾數決定,
那我們來看當1.1111...1,小數點後接52個1,這是精度最大顯示,
要取其最大值那就是向指數借52位,所以最大安全數就等於Math.pow(2,53)-1

同理Number.MIN_SAFE_INTEGER = -9007199254740991;
對最大安全數新增負號即可

重點0.1 + 0.2 怎麼計算的呢?

首先,0.1轉二進位制 :0.0 001100110011001100110011..0011 0011,
改寫為科學計數法表示:1.100110011...0011(0011)*2^-4,
尾數位為52為需要取捨括號中最後一位捨去進1,指數為-4,那個階碼就是-4+1023=1019,
最終浮點數格式:
0-01111111011-1001100110011001100110011001100110011001100110011010
同理0.2可以表示為:
0-01111111100-1001100110011001100110011001100110011001100110011010
最後兩者相加結果為:
0-01111111101-0011001100110011001100110011001100110011001100110100,
指數位為-2,
1.00110011001100110011001100110011001100110011001101(00)*2^-2
所以二進位制表示:
0.0100110011001100110011001100110011001100110011001101(00),
那麼轉為十進位制就是0.1 + 0.2 = 0.30000000000000004 != 0.3