前言
我们都知道,计算机底层是二进制,而二进制是无法精确表示小数的,所以在计算的时候,会出现精度丢失的问题,这里我给出一个解决方案。
问题举例
比如我们的0.1+0.2
在计算中会出现0.30000000000000004
的情况。而不是我们认为的0.3,这是因为
在计算机中,浮点数一般采用 IEEE 754 标准进行表示和计算。按照该标准,浮点数通常由三个部分组成:符号位(S)、指数位(E)和尾数位(M)。具体来说,一个浮点数可以表示为:(-1)^S * M * 2^E。
对于十进制中的 0.1 和 0.2,它们在二进制中无法精确表示。转换成二进制后会出现无限循环的情况。因此,在计算机内部,这两个数会被近似表示。
当计算机进行浮点数加法时,会将参与运算的两个数统一调整为相同的指数,从而进行小数部分的相加。在这个过程中,可能会出现精度损失。
具体到 0.1 + 0.2 的运算中,0.1 转换为二进制后是个无限循环小数,近似值为 0.00011001100110011…(省略后续循环),而 0.2 转换为二进制后是个无限循环小数,近似值为 0.0011001100110011…(省略后续循环)。当这两个近似值进行相加时,结果是 0.010011001100110011…(省略后续循环)。
然而,计算机内部的存储空间是有限的,通常使用特定位数的二进制表示浮点数。在这个有限的存储空间中,只能保留一定精度的有效数字。因此,当计算机将上述近似值存储起来时,存储的实际值会更接近这个近似值。
所以,对于计算机来说,0.1 + 0.2 在存储和计算过程中会存在精度损失,最终结果可能会是一个近似值,而非精确的 0.3。这是浮点数运算在计算机中普遍面临的问题。在编程中,进行浮点数比较时,需要使用适当的误差范围进行判断,而非直接比较它们的值是否相等。
解决方案
将数据变成string进行计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| export const math = {
add(a, b) { let m, n, x; try { m = a.toString().split(".")[1].length; } catch (e) { m = 0; } try { n = b.toString().split(".")[1].length; } catch (e) { n = 0; } x = Math.max(m, n); const transform = (num) => +num.toFixed(x).toString().replace(".", ""); return (transform(+a) + transform(+b)) / 10 ** x; }, accSub(a, b) { return this.accAdd(a, -b); }, multiply(a, b) { let x = 0, a_str = a.toString(), b_str = b.toString(); try { x += a_str.split(".")[1].length; } catch (e) { } try { x += b_str.split(".")[1].length; } catch (e) { } return (+a_str.replace(".", "") * +b_str.replace(".", "")) / 10 ** x; }, divide(a, b) { let x = 0, y = 0, a_str = a.toString(), b_str = b.toString(), a_mul = +a_str.replace(".", ""), b_mul = +b_str.replace(".", ""); try { x = a_str.split(".")[1].length; } catch (e) { } try { y = b_str.split(".")[1].length; } catch (e) { } return (a_mul / b_mul) * 10 ** (y - x); }, };
|
使用方法
1 2
| import {math} from 'math.js' math.add(0.1,0.2)
|
此时得到的结果就是0.3了。
结语
记录一点点,进步一点点。