整數部分除二取餘數, 直到商爲0,逆序排列,小數部分乘2取整,順序排列,直到積中小數部分爲0或者到達要求精度。javascript
8轉爲二進制
8 / 2 = 4...0 取0
4 / 2 = 2...0 取0
2 / 2 = 1...0 取0
1 / 2 = 0...1 取1
二進制結果爲:1000
0.25轉爲二進制
0.25 * 2 = 0.50 取0
0.50 * 2 = 1.00 取1
二進制結果爲:01
因而可得出8.25的二進制表示:1000.01
複製代碼
注意:二進制轉爲十進制不分整數部分與小數部分。java
二進制1000.01轉爲十進制
1 * 2^3 + 0 * 2^2 + 0 * 2^1 + 0 * 2^0 + 0 * 2^-1 + 0 * 2^-2 = 8.25
複製代碼
JavaScript
裏的數字是採用 IEEE 754 標準的 64 位 double 雙精度浮點數函數
sign bit(符號): 用來表示正負號,1位 (0表示正,1表示負)ui
exponent(指數): 用來表示次方數,11位spa
mantissa(尾數): 用來表示精確度,52位3d
對於沒有接觸的讀者來講,以上可能理解起來很模糊,不要緊,接下來咱們用案例具體說明其流程,先看一下上述的十進制數8.25在JS中是如何保存的code
8.25
會被轉化爲二進制的1000.01
;1000.01
可用二進制的科學計數法1.00001 * 2^4
表示;1.00001 * 2^4
的小數部分00001
(二進制)就是mantissa(尾數)了,4
(十進制)加上1023
就是exponent(指數)了(這裏後面講解爲何要加上1023);4
要加上1023
後轉爲二進制10000000011
;8.25
是一個正數,因此符號爲二進制表示爲0
8.25
最終的二進制保存0-10000000011-0000100000000000000000000000000000000000000000000000
注意點:cdn
咱們仍是以8.25的二進制0-10000000011-0000100000000000000000000000000000000000000000000000
來說述blog
1000000001
,轉化爲十進制爲1027
,1027
減去1023
就是咱們實際的指數4
了;0000100000000000000000000000000000000000000000000000
實際是0.00001
(後面的0就不寫了),而後加上咱們忽略的1
,得出1.00001
;0
,因此咱們的數爲正數,得出二進制的科學計數爲1.00001 * 2^4
,接着再轉爲十進制數,就獲得了咱們的8.25
;這裏就要進入咱們的正題了,看懂了前面的原理說明,這部分將會變得很好理解了。ip
要計算0.1+0.2
,首先計算要先讀取到這兩個浮點數
0.1存儲爲64位二進制浮點數
沒有忘記以上步驟吧~
0
,小數部分爲0001100110011001100110011001100110011...
咦,這裏竟然進入了無限循環,那怎麼辦呢?暫時先無論;1.100110011001100110011001100110011... * 2^-4
;-4 + 1023 = 1019
,轉化位11位二進制數01111111011
;1001100110011001100110011001100110011001100110011010
0-01111111011-1001100110011001100110011001100110011001100110011010
同上,0.2存儲爲64位二進制浮點數:0-01111111100-1001100110011001100110011001100110011001100110011010
讀取到兩個浮點數的64爲二進制後,再將其轉化爲可計算的二進制數
1.1001100110011001100110011001100110011001100110011010 * 2^(1019 - 1023)
——0.00011001100110011001100110011001100110011001100110011010
;1.1001100110011001100110011001100110011001100110011010 * 2^(1020 - 1023)
——0.0011001100110011001100110011001100110011001100110011010
;接着將兩個浮點數的二進制數進行加法運算,得出0.0100110011001100110011001100110011001100110011001100111
轉化爲十進制數即爲0.30000000000000004
不難看出,精度缺失是在存儲這一步就丟失了,後面的計算只是在不精準的值上進行的運算。
對於小數或者整數的簡單運算可以下解決:
function numAdd(num1, num2) {
let baseNum, baseNum1, baseNum2;
try {
baseNum1 = String(num1).split(".")[1].length;
} catch (e) {
baseNum1 = 0;
}
try {
baseNum2 = String(num2).split(".")[1].length;
} catch (e) {
baseNum2 = 0;
}
baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
return (num1 * baseNum + num2 * baseNum) / baseNum;
};
複製代碼
如:0.1 + 0.2
經過函數處理後,至關於 (0.1 * 10 + 0.2 * 10) / 10
可是如同咱們前面所瞭解的,浮點數在存儲的時候就已經丟失精度了,因此浮點數乘以一個基數仍然會存在精度缺失問題,好比2500.01 * 100 = 250001.00000000003
, 因此咱們能夠在以上函數的結果之上使用toFixed(),保留須要的小數位數。
一些複雜的計算,能夠引入一些庫進行解決。