如何处理JavaScript中的浮点数精度?

我有以下虚拟测试脚本:

function test() {
  var x = 0.1 * 0.2;
  document.write(x);
}
test();

这将打印结果,0.020000000000000004而仅打印结果0.02(如果使用计算器)。据我了解,这是由于浮点乘法精度的错误。

有没有人有一个好的解决方案,这样在这种情况下我可以获得正确的结果0.02我知道还有类似的函数,toFixed或者四舍五入是另一种可能性,但是我真的想在不进行任何四舍五入的情况下打印出完整的数字。只想知道你们中的一个人是否有一些不错的,优雅的解决方案。

当然,否则我将四舍五入到大约10位数字。

Davaid泡芙2020/03/12 11:13:48

同时添加两个浮点值时,它永远不会给出精确的值,因此我们需要将此值固定为一定的数字,这将有助于我们进行比较。

console.log((parseFloat(0.1)+ parseFloat(0.2))。toFixed(1)== parseFloat(0.3).toFixed(1));
米亚小小神乐2020/03/12 11:13:48

这对我有用:

function round_up( value, precision ) { 
    var pow = Math.pow ( 10, precision ); 
    return ( Math.ceil ( pow * value ) + Math.ceil ( pow * value - Math.ceil ( pow * value ) ) ) / pow; 
}

round_up(341.536, 2); // 341.54
番长前端2020/03/12 11:13:48

采用

var x = 0.1*0.2;
 x =Math.round(x*Math.pow(10,2))/Math.pow(10,2);
2020/03/12 11:13:48

不太优雅,但能胜任工作(删除结尾的零)

var num = 0.1*0.2;
alert(parseFloat(num.toFixed(10))); // shows 0.02
阿飞神无2020/03/12 11:13:48

使用编号(1.234443).toFixed(2); 它将打印1.23

function test(){
    var x = 0.1 * 0.2;
    document.write(Number(x).toFixed(2));
}
test();
伽罗西门小胖2020/03/12 11:13:48

我在mod 3上遇到了一个令人讨厌的舍入错误问题。有时,当我得到0时,我会得到.000 ... 01。这很容易处理,只需测试<= .01。但是有时候我会得到2.99999999999998。哎哟!

BigNumbers解决了该问题,但引入了另一个具有讽刺意味的问题。尝试将8.5加载到BigNumbers中时,我得知它实际上是8.4999…,并且有15个以上的有效数字。这意味着BigNumbers无法接受它(我相信我提到这个问题有点讽刺意味)。

解决讽刺问题的简单方法:

x = Math.round(x*100);
// I only need 2 decimal places, if i needed 3 I would use 1,000, etc.
x = x / 100;
xB = new BigNumber(x);
斯丁理查德2020/03/12 11:13:48

您不能完全使用二进制浮点类型(这是ECMAScript用来表示浮点值)来精确表示大多数小数部分。因此,除非您使用任意精度的算术类型或基于十进制的浮点类型,否则没有一个好的解决方案。例如,Windows附带的Calculator应用程序现在使用任意精度算法来解决此问题

蛋蛋L2020/03/12 11:13:48

在此处输入图片说明

    You can use library https://github.com/MikeMcl/decimal.js/. 
    it will   help  lot to give proper solution. 
    javascript console output 95 *722228.630 /100 = 686117.1984999999
    decimal library implementation 
    var firstNumber = new Decimal(95);
    var secondNumber = new Decimal(722228.630);
    var thirdNumber = new Decimal(100);
    var partialOutput = firstNumber.times(secondNumber);
    console.log(partialOutput);
    var output = new Decimal(partialOutput).div(thirdNumber);
    alert(output.valueOf());
    console.log(output.valueOf())== 686117.1985
小哥达蒙卡卡西2020/03/12 11:13:48

尝试一下我的Chiliadic算术库,您可以在这里看到如果您想要更高的版本,我可以帮助您。

Itachi小宇宙2020/03/12 11:13:48

看一下定点算法如果您要操作的数字范围较小(例如,货币),则可能会解决您的问题。我会将其四舍五入为几个十进制值,这是最简单的解决方案。

乐猪猪2020/03/12 11:13:48

没错,原因是浮点数的精度有限。将有理数存储为两个整数的除法,在大多数情况下,您将能够存储数字而不会造成任何精度损失。在打印时,您可能希望将结果显示为分数。通过我提出的代表性,它变得微不足道。

当然,这对于无理数不会有太大帮助。但是,您可能希望以它们引起最少问题的方式优化计算(例如,检测诸如的情况)sqrt(3)^2)

村村L2020/03/12 11:13:48

为了避免这种情况,您应该使用整数值而不是浮点数。因此,当您要使用2个位置精度* 100时,请为3个位置使用1000。在显示时,请使用格式化程序放置分隔符。

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

老丝2020/03/12 11:13:48

您可以使用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;
Jim老丝梅2020/03/12 11:13:48

在不同语言,处理器和操作系统的浮点实现中,您获得的结果是正确且相当一致的-唯一改变的是浮点实际上是双精度(或更高)时的不准确性级别。

0.1的二进制浮点数就像1/3的十进制数(即永远为0.3333333333333 ...),没有精确的处理方法。

如果要处理浮点数,则总是会期望出现较小的舍入误差,因此,您还必须始终将显示的结果舍入到合理的值。作为回报,您将获得非常非常快速且强大的算法,因为所有计算都在处理器的本机二进制文件中。

大多数情况下,解决方案不是切换到定点算法,主要是因为它要慢得多,而且99%的时间中您根本不需要精度。如果您要处理的是确实需要这种准确性的东西(例如金融交易),那么Javascript可能不是最佳的使用工具(因为您要强制使用定点类型,因此使用静态语言可能会更好) )。

您正在寻找一种优雅的解决方案,然后恐怕就是这样:浮点数很快,但是舍入误差很小-在显示结果时总是舍入到合理的值。

西里神奇泡芙2020/03/12 11:13:48

0.6 * 3太棒了!))对我来说,这很好用:

function dec( num )
{
    var p = 100;
    return Math.round( num * p ) / p;
}

非常非常简单))

村村AL2020/03/12 11:13:48

phpjs.org上的round()函数很好地工作:http ://phpjs.org/functions/round

num = .01 + .06;  // yields 0.0699999999999
rnum = round(num,12); // yields 0.07
乐米亚2020/03/12 11:13:48

注意,对于一般用途,此行为可能是可以接受的。
比较那些浮点值以确定适当的操作时会出现问题。
随着ES6的到来,Number.EPSILON定义了一个新的常量来确定可接受的误差范围:
因此,而不是像这样执行比较

0.1 + 0.2 === 0.3 // which returns false

您可以定义一个自定义比较功能,如下所示:

function epsEqu(x, y) {
    return Math.abs(x - y) < Number.EPSILON;
}
console.log(epsEqu(0.1+0.2, 0.3)); // true

资料来源:http : //2ality.com/2015/04/numbers-math-es6.html#numberepsilon

Near小哥Green2020/03/12 11:13:48

您只需要确定自己实际需要多少个小数位-不能再吃蛋糕了:-)

每次执行进一步的操作时,都会累积数值误差,如果您不尽早将其切断,它将不断增加。数字库显示的结果看起来很干净,每一步都将最后两位数字截断,数字协处理器出于相同的原因也具有“正常”和“完全”长度。Cuf-off对处理器而言很便宜,但对您而言在脚本中非常昂贵(乘法,除法和使用pov(...))。好的数学库会提供floor(x,n)为您做截止。

因此,至少您应该使用pov(10,n)来使全局变量/常量成为常量-这意味着您决定了所需的精度:-)然后执行:

Math.floor(x*PREC_LIM)/PREC_LIM  // floor - you are cutting off, not rounding

您还可以继续进行数学运算,并且仅在最后进行截止-假设您仅显示结果而不对if-s进行运算。如果可以这样做,那么.toFixed(...)可能会更有效。

如果您要进行if-s /比较,并且不希望减少,则还需要一个小的常数,通常称为eps,该常数比最大期望误差高一个小数位。假设您的截止数是最后两位小数-那么您的eps在最后一位的第三位(最低有效位第三),您可以使用它来比较结果是否在预期的eps范围内(0.02 -eps <0.1 * 0.2 <0.02 + eps)。

镜风Davaid2020/03/12 11:13:48
var times = function (a, b) {
    return Math.round((a * b) * 100)/100;
};

- -要么 - -

var fpFix = function (n) {
    return Math.round(n * 100)/100;
};

fpFix(0.1*0.2); // -> 0.02

- -也 - -

var fpArithmetic = function (op, x, y) {
    var n = {
            '*': x * y,
            '-': x - y,
            '+': x + y,
            '/': x / y
        }[op];        

    return Math.round(n * 100)/100;
};

---如-

fpArithmetic('*', 0.1, 0.2);
// 0.02

fpArithmetic('+', 0.1, 0.2);
// 0.3

fpArithmetic('-', 0.1, 0.2);
// -0.1

fpArithmetic('/', 0.2, 0.1);
// 2
西门路易2020/03/12 11:13:48

您正在寻找sprintfJavaScript 实现,以便您可以以期望的格式写出带有小错误的浮点数(因为它们以二进制格式存储)。

尝试javascript-sprintf,您将这样称呼它:

var yourString = sprintf("%.2f", yourNumber);

以小数点后两位小数的形式打印您的数字。

如果您不想仅出于浮点舍入到给定精度的目的而添加更多文件,也可以将 Number.toFixed()用于显示目的。

西门JinJin2020/03/12 11:13:48

我喜欢Pedro Ladaria的解决方案,并使用类似的方法。

function strip(number) {
    return (parseFloat(number).toPrecision(12));
}

与Pedros解决方案不同,此函数将舍入为0.999 ...重复,并且精确到最低有效数字的正负一一。

注意:处理32或64位浮点数时,应使用toPrecision(7)和toPrecision(15)以获得最佳结果。有关此问题的信息,请参见此问题

YOC644194782020/03/12 11:13:48

您只执行乘法吗?如果是这样,那么您可以利用有关十进制算术的巧妙秘密。就是那个NumberOfDecimals(X) + NumberOfDecimals(Y) = ExpectedNumberOfDecimals就是说,如果有的0.123 * 0.12话,我们知道会有5个小数位,因为0.123有3个小数位和0.122 个小数位因此,如果JavaScript为我们提供了一个数字,例如0.014760000002我们可以安全地舍入到小数点后第五位,而不必担心失去精度。