十进制转二进制
用Number.prototype.toString()方法;
二进制转十进制
用parseInt方法
JS计算小数精度丢失解决方案
问题的原因
产生浮点数计算精度不准确的原因: 在计算机角度,计算机算的是二进制,而不是十进制。二进制后变成了无线不循环的数,而计算机可支持浮点数的小数部分可支持到52位,所有两者相加,在转换成十进制,得到的数就不准确了,加减乘除运算原理一样。
最通俗的解释
比如一个数 1÷3=0.33333333...... 大家都知道3会一直无限循环,数学可以表示,但是计算机要存储,方便下次取出来再使用,但0.333333...... 这个数 无限循环,你让计算机怎么存储?计算机再大的内存它也存不下,对吧! 所以不能存储一个相对于数学来说的值,只能存储一个近似值,所以当计算机存储后再取出来用时就会出现精度问题。
为什么0.1 +0.2===0.30000000000000004 ?
我们来推导一下0.1
0.1>>>二进制>>>科学记数法>>实际存储时的形式[64位] 符号位+(指数位+指数偏移量)+小数部分) 即:
0.1>>>0.0001100110011001100110011001100110011001100110011001101>>> 1.100110011001100110011001100110011001100110011001101 * 2^(-4)>>>
0011111110111001100110011001100110011001100110011001100110011010
同理,0.2
0.2>>二进制>>>科学记数法>>实际存储时的形式64位 即
0.2>>>0.001100110011001100110011001100110011001100110011001101>>>1.100110011001100110011001100110011001100110011001101 * 2^(-3)>>>0011111111001001100110011001100110011001100110011001100110011010
可以看出来在转换为二进制时
0.1 >>> 0.0001 1001 1001 1001...(1001无限循环)
0.2 >>> 0.0011 0011 0011 0011...(0011无限循环)
就像一些无理数不能有限表示,如 圆周率 3.1415926...,1.3333... 等,在转换为二进制的科学记数法的形式时只保留64位有效的数字,此时只能模仿十进制进行四舍五入了,但是二进制只有 0 和 1 两个,于是变为 0 舍 1 入。在这一步出现了错误,那么一步错步步错,那么在计算机存储小数时也就理所应当的出现了误差。这即是计算机中部分浮点数运算时出现误差,这就是丢失精度的根本原因
将0.1和0.2的二进制形式按实际展开,末尾补零相加,结果如下
0.00011001100110011001100110011001100110011001100110011010
+0.00110011001100110011001100110011001100110011001100110100
=0.01001100110011001100110011001100110011001100110011001110
0.1+0.2 >> 0.0100 1100 1100 1100...(1100无限循环)
则0.1 + 0.2的结果的二进制数科学记数法表示为为1.001100110011001100110011001100110011001100110011010 * 2^(-2), 省略尾数最后的0,即 1.00110011001100110011001100110011001100110011001101 * 2^(-2), 因此(0.1+0.2)实际存储时的形式是 0011111111010011001100110011001100110011001100110011001100110100
因计算机存储位数的限制而截断的二进制数字,再转换为十进制,就成了0.30000000000000004
推导完成
总结:计算机存储双精度浮点数需要先把十进制数转换为二进制的科学记数法的形式,然后计算机以自己的规则{符号位+(指数位+指数偏移量的二进制)+小数部分}存储二进制的科学记数法,因为存储时有位数限制(64位),并且某些十进制的浮点数在转换为二进制数时会出现无限循环,会造成二进制的舍入操作(0舍1入),当再转换为十进制时就造成了计算误差。
解决方案
toFixed() 方法
toFixed() 方法使用定点表示法来格式化一个数,会对结果进行四舍五入。语法为:
参数 digits
表示小数点后数字的个数;介于 0 到 20 (包括)之间,实现环境可能支持更大范围。如果忽略该参数,则默认为 0。