我有以下虚拟测试脚本:

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

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

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

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


当前回答

如果需要进行任意精度浮点计算,可以使用我的NPM库gmp-wasm,它基于gmp+MPFR库。您可以轻松设置所需的任何精度,并以固定精度返回结果。

<script src=“https://cdn.jsdelivr.net/npm/gmp-wasm“></script><脚本>gmp.init().then(({getContext})=>{const ctx=getContext({precisionBits:100});常量结果=ctx.Float('0.1').mul(ctx.Flat('0.2'));document.write(`0.1*0.2=`+result.toFixed(2));ctx.destroy();});</script>

其他回答

为了避免这种情况,您应该使用整数值而不是浮点。因此,当您希望使用值*100进行2个位置的精度工作时,对于3个位置使用1000。显示时,使用格式化程序放入分隔符。

许多系统省略了以这种方式处理小数。这就是为什么许多系统使用美分(作为整数)而不是美元/欧元(作为浮点)的原因。

从浮点指南:

我可以做什么来避免这个问题?这取决于什么样的你正在做的计算。如果你真的需要你的结果精确相加,特别是当你使用货币:使用特殊小数数据类型。如果你只是不想看到所有多余的小数点:将结果四舍五入设置为固定格式小数位数显示它。如果没有可用的十进制数据类型,另一种方法是工作使用整数,例如,do money完全以美分计算。但是这是更多的工作,有一些缺点。

注意,第一点仅适用于您确实需要特定的精确小数行为的情况。大多数人不需要这样做,他们只是对自己的程序无法正确处理1/10这样的数字感到愤怒,而没有意识到如果出现1/3这样的错误,他们甚至不会眨眼。

如果第一点确实适用于您,请使用BigDecimal for JavaScript或DecimalJS,这实际上解决了问题,而不是提供不完美的解决方法。

这个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>

问题

浮点不能精确存储所有十进制值。因此,当使用浮点格式时,输入值将始终存在舍入错误。输入端的错误当然会导致输出端的错误。在离散函数或运算符的情况下,函数或运算符离散的点附近的输出可能存在很大差异。

浮点值的输入和输出

因此,在使用浮点变量时,您应该始终注意这一点。并且,在显示之前,无论您希望从带有浮点的计算中获得什么输出,都应该在显示之前进行格式化/调整。当只使用连续函数和运算符时,舍入到所需的精度通常是可行的(不要截断)。用于将浮点数转换为字符串的标准格式功能通常会为您提供这一功能。由于舍入增加了一个误差,该误差可能导致总误差超过所需精度的一半,因此应根据输入的预期精度和输出的预期精度对输出进行校正。你应该

将输入舍入到预期精度,或确保不能输入精度更高的值。在舍入/格式化输出之前,为输出添加一个小值,该值小于或等于所需精度的1/4,并且大于输入和计算期间舍入误差导致的最大预期误差。如果不可能,则所用数据类型的精度组合不足以为计算提供所需的输出精度。

这两件事通常都没有做,在大多数情况下,不做它们所造成的差异太小,对大多数用户来说都不重要,但我已经有了一个项目,如果没有这些更正,输出不会被用户接受。

离散函数或运算符(如模)

当涉及离散运算符或函数时,可能需要额外的校正以确保输出符合预期。舍入和在舍入之前添加小的修正并不能解决这个问题。应用离散函数或运算符后,可能需要立即对中间计算结果进行特殊检查/校正。对于特定的情况(模运算符),请参阅我对问题的回答:为什么模运算符在javascript中返回小数?

最好避免出现问题

通过使用数据类型(整数或定点格式)进行这样的计算,通常可以更有效地避免这些问题,这样可以存储预期的输入,而不会出现舍入误差。这方面的一个例子是,您永远不应该在财务计算中使用浮点值。

我不太擅长编程,但对这个主题很感兴趣,所以我试图了解如何在不使用任何库或脚本的情况下解决这个问题

我在草稿栏上写的

var toAlgebraic = function(f1, f2) {
    let f1_base = Math.pow(10, f1.split('.')[1].length);
    let f2_base = Math.pow(10, f2.split('.')[1].length);
    f1 = parseInt(f1.replace('.', ''));
    f2 = parseInt(f2.replace('.', ''));

    let dif, base;
    if (f1_base > f2_base) {
        dif = f1_base / f2_base;
        base = f1_base;
        f2 = f2 * dif;
    } else {
        dif = f2_base / f1_base;
        base = f2_base;
        f1 = f1 * dif;
    }

    return (f1 * f2) / base;
};

console.log(0.1 * 0.2);
console.log(toAlgebraic("0.1", "0.2"));

您可能需要重构此代码,因为我不擅长编程:)