调整计算精度
问题由来:js 数字存储采用 IEEE754 标准,该标准用 64 位二进制数存储一个浮点数,即一个数字占用的内存是 8bytes,因此在存储一个无限大的数字时就会存在精度损失。对于像 0.1 这样的浮点数来说,无法用二进制的方式精确表示,这是由计算方式本身决定的,因此这部分浮点数的存储精度丢失是必然的,和语言、存储方式、内存大小没有关系。当然,像 0.5 这种可以用二进制精确表示,并且大小不超过 2** 53 - 1 的浮点数是可以精确存储的。IEEE754 的存储方式是,最高位是符号位,0 表示正,1表示负;接下来是 11 位指数位;接下来是 52 位小数位。二进制的科学计数法,它的整数部分固定是 1,所以 1 个整数位加上 52 的小数位,最大可以表示 2**53 - 1,因此即使是整数,如果超出这个范围,存储精度也是要损失的。
思路:刚才说一个无穷数的存储是有问题的,如果我们把浮点数当作整数计算,计算完成后再转换为字符串类型的小数存储是不是就可以了。
实现:
1 function add(a, b) { 2 a = a.toString(), 3 b = b.toString(); 4 const r0 = a.includes('.') ? a.length - (a.indexOf('.') + 1) : ((a += '.'), 0), 5 r = b.includes('.') ? b.length - (b.indexOf('.') + 1) : ((b += '.'), 0); 6 7 if (r0 > r) { 8 b += '0'.repeat(r0 - r); 9 } 10 11 if (r > r0) { 12 a += '0'.repeat(r - r0); 13 } 14 15 return (+a.replace('.', '') + +b.replace('.', '')) / 10 ** Math.max(r0, r); 16 } 17 18 function sub(a, b) { 19 return add(a, -b); 20 } 21 22 function mul(a, b) { 23 a = a.toString(), 24 b = b.toString(); 25 const r0 = a.includes('.') ? a.length - (a.indexOf('.') + 1) : ((a += '.'), 0), 26 r = b.includes('.') ? b.length - (b.indexOf('.') + 1) : ((b += '.'), 0); 27 28 if (r0 > r) { 29 b += '0'.repeat(r0 - r); 30 } 31 32 if (r > r0) { 33 a += '0'.repeat(r - r0); 34 } 35 36 return (+a.replace('.', '') * +b.replace('.', '')) / (10 ** Math.max(r0, r)) ** 2; 37 } 38 39 function div(a, b) { 40 a = a.toString(), 41 b = b.toString(); 42 const r0 = a.includes('.') ? a.length - (a.indexOf('.') + 1) : ((a += '.'), 0), 43 r = b.includes('.') ? b.length - (b.indexOf('.') + 1) : ((b += '.'), 0); 44 45 if (r0 > r) { 46 b += '0'.repeat(r0 - r); 47 } 48 49 if (r > r0) { 50 a += '0'.repeat(r - r0); 51 } 52 53 return +a.replace('.', '') / +b.replace('.', ''); 54 } 55 56 export { 57 add, 58 sub, 59 mul, 60 div 61 };