整数部分除二取余数, 直到商为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(),保留须要的小数位数。
一些复杂的计算,能够引入一些库进行解决。