我有以下虚拟测试脚本:

函数测试(){变量x=0.1*0.2;document.write(x);}测试();

这将打印结果0.020000000000000004,而它应该只打印0.02(如果您使用计算器)。据我所知,这是由于浮点乘法精度的错误。

有没有人有一个好的解决方案,在这种情况下,我得到了正确的结果0.02?我知道还有一些函数,比如toFixed或舍入,这是另一种可能,但我真的希望在不进行任何切割和舍入的情况下打印整个数字。我只是想知道你们中的一个人是否有一些好的、优雅的解决方案。

当然,否则我会舍入到10位数左右。


当前回答

我通常用这样的东西。

function pf(n) {
    return Math.round(n * 1e15) / 1e15;
}

我并不认为这在任何方面都是最佳的,但我喜欢它的简单性。它将数字四舍五入到小数点后15位左右。我没有看到它返回不准确的浮点数,但奇怪的是,当我在末尾使用*1e-15时,它已经这样做了,但没有使用这种方法。

这种解决方案可能更适合于随意使用,而不是精确的数学使用,因为精确错误会扰乱代码。

其他回答

这个npm库是为了从我自己的用例中解决这个问题而构建的,并且已经在大规模生产中部署。希望对其他人有所帮助。

npm i jsbi计算器

它基于GoogleChromeLabs/jsbi项目,使用字符串化表达式执行任意有理计算,兼容ie11。

浏览器的用法:

<!DOCTYPE html><html lang=“en”><head><meta charset=“UTF-8”/><meta http equiv=“X-UA-Compatible”content=“IE=edge”/><meta name=“viewport”content=“width=设备宽度,初始比例=1.0”/><title>Jsbi计算器测试</title><script src=“https://cdn.jsdelivr.net/npm/jsbi-calculator/dist/jsbi-calculator-umd.js“></script></head><body></body><script type=“text/javascript”>常量表达式One=“((10*(24/((9+3)*(-2))))+17)+5.2”;const resultOne=JBC.calculator(expressionOne);console.log(resultOne);// -> '12.2'const userAgent=navigator.userAgent;常量为IE11=userAgent.indexOf(“Trident”)>-1&&userAgent.inindexOf(”rv:11.0“)>-1;let max;//MAX_SAFE_INTEGER在IE11中不可用max=isIE11?“9007199254740991”:字符串(编号.MAX_SAFE_INTEGER);console.log(最大值);// -> '9007199254740991'常量表达式Two=max+“+2.2”;const resultTwo=JBC.calculator(表达式Two);console.log(resultTwo);// -> '9007199254740993.2'</script></html>

如果您想在小操作中绕过此问题,可以使用parseFloat()和toFixed():

a = 0.1;
b = 0.2;

a + b = 0.30000000000000004;

c = parseFloat((a+b).toFixed(2));

c = 0.3;

a = 0.3;
b = 0.2;

a - b = 0.09999999999999998;

c = parseFloat((a-b).toFixed(2));

c = 0.1;

首先将两个数字都设为整数,执行表达式,然后对结果进行除法运算,以返回小数点:

function evalMathematicalExpression(a, b, op) {
    const smallest = String(a < b ? a : b);
    const factor = smallest.length - smallest.indexOf('.');

    for (let i = 0; i < factor; i++) {
        b *= 10;
        a *= 10;
    }

    a = Math.round(a);
    b = Math.round(b);
    const m = 10 ** factor;
    switch (op) {
        case '+':
            return (a + b) / m;
        case '-':
            return (a - b) / m;
        case '*':
            return (a * b) / (m ** 2);
        case '/':
            return a / b;
    }

    throw `Unknown operator ${op}`;
}

几个操作的结果(排除的数字是eval的结果):

0.1 + 0.002   = 0.102 (0.10200000000000001)
53 + 1000     = 1053 (1053)
0.1 - 0.3     = -0.2 (-0.19999999999999998)
53 - -1000    = 1053 (1053)
0.3 * 0.0003  = 0.00009 (0.00008999999999999999)
100 * 25      = 2500 (2500)
0.9 / 0.03    = 30 (30.000000000000004)
100 / 50      = 2 (2)

看看固定点算法。如果你想操作的数字范围很小(例如货币),它可能会解决你的问题。我会将其舍入为几个十进制值,这是最简单的解决方案。

优雅、可预测、可重复使用

让我们以一种优雅的、可重用的方式来处理这个问题。通过在数字、公式或内置Math函数的末尾添加.dedecimal,以下七行将允许您访问任意数字所需的浮点精度。

//首先扩展本机Number对象以处理精度。这将填充//所有数学运算的功能。Object.defineProperty(Number.prototype,“decimal”{get:函数decimal(){Number.precision=数字中的“精度”?数字精度:3;var f=数学.pow(10,数字精度);return Math.round(this*f)/f;}});//现在让我们来看看它是如何通过调整我们的全球精度水平和//检查我们的结果。console.log(“'1/3+1/3+1/3=1'对吗?”);console.log((0.3333+0.3333+0.3333).decimal==1);//真的console.log(0.3333.decimal);//0.333-一个原始的4位小数,修剪为3。。。数字精度=3;console.log(“精度:3”);console.log((0.8+0.2).dedecimal);//1.console.log((0.08+0.02).decimal);//0.1控制台日志((0.008+0.002)十进制);//0.01console.log((0.0008+0.0002).decimal);//0.001数字精度=2;console.log(“精度:2”);console.log((0.8+0.2).dedecimal);//1.console.log((0.08+0.02).decimal);//0.1控制台日志((0.008+0.002)十进制);//0.01console.log((0.0008+0.0002).decimal);//0数字精度=1;console.log(“精度:1”);console.log((0.8+0.2).dedecimal);//1.console.log((0.08+0.02).decimal);//0.1控制台日志((0.008+0.002)十进制);//0console.log((0.0008+0.0002).decimal);//0数字精度=0;console.log(“精度:0”);console.log((0.8+0.2).dedecimal);//1.console.log((0.08+0.02).decimal);//0控制台日志((0.008+0.002)十进制);//0console.log((0.0008+0.0002).decimal);//0

干杯