我有以下虚拟测试脚本:

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

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

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

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


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


Use

var x = 0.1*0.2;
 x =Math.round(x*Math.pow(10,2))/Math.pow(10,2);

不优雅,但能完成任务(删除尾随零)

var num = 0.1*0.2;
alert(parseFloat(num.toFixed(10))); // shows 0.02

试试我的千年算术库,你可以在这里看到。如果你想要更高版本,我可以给你买一个。


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


你只需要决定你实际想要多少个小数位数-不能既吃蛋糕又吃蛋糕:-)

随着每一次进一步的操作,数值误差都会累积,如果你不及早切断它,它只会增长。数值库显示的结果看起来很干净,只需在每一步中删除最后2位数字,数值协处理器也有“正常”和“完整”长度,原因相同。对于一个处理器来说,截断是便宜的,但对于脚本(乘法、除法和使用pov(…))来说非常昂贵。好的数学库将提供floor(x,n)来为您进行截断。

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

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

你也可以继续做数学运算,只在最后截止——假设你只显示结果而不做if-s。如果你能做到这一点,那么.toFixed(…)可能会更有效率。

如果您正在进行If-s/比较,并且不想截断,那么还需要一个小常数,通常称为eps,它比最大预期误差高一个小数位。假设你的截止值是最后两位小数,那么你的eps在倒数第三位(第三位最不重要)有1,你可以用它来比较结果是否在预期的eps范围内(0.02-eps<0.1*0.2<0.02+eps)。


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

试试javascript sprintf,你可以这样称呼它:

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

将数字打印为带有两个小数点的浮点数。

如果您不希望仅为浮点舍入到给定精度而包含更多文件,也可以使用Number.toFixed()进行显示。


你是对的,原因是浮点数的精度有限。将有理数存储为两个整数的除法,在大多数情况下,您可以存储数字而不损失任何精度。在打印时,您可能希望将结果显示为分数。有了我提出的表示法,它就变得微不足道了。

当然,这对非理性数字没有太大帮助。但您可能希望优化您的计算,使其产生的问题最少(例如,检测sqrt(3)^2等情况)。


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

您得到的结果是正确的,并且在不同语言、处理器和操作系统中的浮点实现之间相当一致——唯一改变的是当浮点实际上是双倍(或更高)时的不准确程度。

二进制浮点中的0.1与十进制中的1/3相似(即永远为0.333333333333333…),只是没有准确的方法来处理它。

如果您处理的是浮点数,那么总是会出现小的舍入误差,因此您也必须将显示的结果舍入到合理的值。作为回报,您可以得到非常快速和强大的算法,因为所有计算都是在处理器的本地二进制中进行的。

大多数情况下,解决方案不是切换到定点运算,主要是因为它速度慢得多,99%的时间你不需要精度。如果你处理的东西确实需要这样的准确性(例如金融交易),那么无论如何,Javascript可能不是最好的工具(因为你想要强制执行定点类型,静态语言可能更好)。

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


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

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


从浮点指南:

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

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

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


你只是在做乘法吗?如果是这样的话,那么你可以利用一个关于十进制算术的简单秘密。即NumberOfDecimals(X)+NumberOfDecimals(Y)=预期的NumberOf小数。也就是说,如果我们有0.123*0.12,那么我们知道会有5位小数,因为0.123有3位小数,0.12有2位小数。因此,如果JavaScript给我们一个像0.014760000002这样的数字,我们可以安全地四舍五入到小数点后的第五位,而不用担心丢失精度。


我喜欢佩德罗·拉达里亚的解决方案,并使用类似的方法。

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

与Pedros解决方案不同,这将向上取整0.999……重复,并精确到最低有效位数的正负一。

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


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

num = .01 + .06;  // yields 0.0699999999999
rnum = round(num,12); // yields 0.07

我有一个变通办法。例如,仅与10E^x相乘不适用于1.1。

function sum(a,b){
    var tabA = (a + "").split(".");
    var tabB = (b + "").split(".");
    decA = tabA.length>1?tabA[1].length:0;
    decB = tabB.length>1?tabB[1].length:0;
    a = (tabA[0]+tabA[1])*1.0;
    b = (tabB[0]+tabB[1])*1.0;
    var diff = decA-decB;
    if(diff >0){
        //a has more decimals than b
        b=b*Math.pow(10,diff);
        return (a+b)/Math.pow(10,decA);
    }else if (diff<0){
        //a has more decimals than b
        a=a*Math.pow(10,-diff);
                return (a+b)/Math.pow(10,decB);
    }else{
        return (a+b)/Math.pow(10,decA);
    }       
}

可怕但有效:)


您可以使用正则表达式检查数字是否以0的长字符串结尾,后跟一个小余数:

// using max number of 0s = 8, maximum remainder = 4 digits
x = 0.1048000000000051
parseFloat(x.toString().replace(/(\.[\d]+[1-9])0{8,}[1-9]{0,4}/, '$1'), 10)
// = 0.1048

对于数学倾向:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

推荐的方法是使用校正因子(乘以适当的10次方,以便在整数之间进行运算)。例如,在0.1*0.2的情况下,校正系数为10,您正在执行以下计算:

> var x = 0.1
> var y = 0.2
> var cf = 10
> x * y
0.020000000000000004
> (x * cf) * (y * cf) / (cf * cf)
0.02

一个(非常快速的)解决方案看起来像:

var _cf = (function() {
  function _shift(x) {
    var parts = x.toString().split('.');
    return (parts.length < 2) ? 1 : Math.pow(10, parts[1].length);
  }
  return function() { 
    return Array.prototype.reduce.call(arguments, function (prev, next) { return prev === undefined || next === undefined ? undefined : Math.max(prev, _shift (next)); }, -Infinity);
  };
})();

Math.a = function () {
  var f = _cf.apply(null, arguments); if(f === undefined) return undefined;
  function cb(x, y, i, o) { return x + f * y; }
  return Array.prototype.reduce.call(arguments, cb, 0) / f;
};

Math.s = function (l,r) { var f = _cf(l,r); return (l * f - r * f) / f; };

Math.m = function () {
  var f = _cf.apply(null, arguments);
  function cb(x, y, i, o) { return (x*f) * (y*f) / (f * f); }
  return Array.prototype.reduce.call(arguments, cb, 1);
};

Math.d = function (l,r) { var f = _cf(l,r); return (l * f) / (r * f); };

在这种情况下:

> Math.m(0.1, 0.2)
0.02

我绝对建议使用SinfulJS这样的测试库


这对我有用:

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

使用以下功能输出:

var toFixedCurrency = function(num){
    var num = (num).toString();
    var one = new RegExp(/\.\d{1}$/).test(num);
    var two = new RegExp(/\.\d{2,}/).test(num);
    var result = null;

    if(one){ result = num.replace(/\.(\d{1})$/, '.$10');
    } else if(two){ result = num.replace(/\.(\d{2})\d*/, '.$1');
    } else { result = num*100; }

    return result;
}

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

test();

注意FixedCurrency(x)的输出。


使用编号(1.234443)。至固定(2);它将打印1.23

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

我发现BigNumber.js符合我的需求。

用于任意精度十进制和非十进制算术的JavaScript库。

它有很好的文档记录,作者对反馈非常认真。

同一作者还有其他两个类似的库:

Big.js

一个用于任意精度十进制运算的小型快速JavaScript库。bignumber.js的小妹妹。

和Decimal.js

JavaScript的任意精度Decimal类型。

下面是一些使用BigNumber的代码:

$(函数){var product=BigNumber(.1).times(.2);$('#product').text(product);var sum=BigNumber(.1).加号(.2);$('#sum').text(sum);});<script src=“https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js“></script><!-- 1.4.1不是当前版本,但适用于本示例。--><script src=“http://cdn.bootcss.com/bignumber.js/1.4.1/bignumber.min.js“></script>.1&次。2=<span id=“product”></span><br>.1加上。2=<span id=“sum”></span><br>


我在MOD3中遇到了一个严重的舍入错误问题。有时当我应该得到0时,我会得到.000…01。这很容易处理,只需测试<=0.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);

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

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

非常简单))


如果您想在小操作中绕过此问题,可以使用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;

    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

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

console.log((parseFloat(0.1)+parseFlat(0.2)).toFixed(1)==parseFloat(0.3).toFixed(1));


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

我在草稿栏上写的

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"));

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


问题

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

浮点值的输入和输出

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

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

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

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

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

最好避免出现问题

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


注意,对于一般用途,这种行为可能是可以接受的。当比较这些浮点值以确定适当的操作时,会出现问题。随着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


要处理任意浮点数,请执行以下操作:

function shorten(num) {
    num += 0.000000001;// to deal with "12.03999999997" form
    num += '';
    return num.replace(/(\.\d*?)0{5,}\d+$/, '$1') * 1;
}

console.log(1.2+1.9===1.3+1.8);// false
console.log(shorten(1.2+1.9)===shorten(1.3+1.8));// true

令人惊讶的是,这个函数还没有发布,尽管其他函数也有类似的变体。它来自MDN web docs for Math.rround()。它简洁,允许不同的精度。

function precisionRound(number, precision) {
    var factor = Math.pow(10, precision);
    return Math.round(number * factor) / factor;
}

console.log(precisionRound(1234.5678, 1));
// expected output: 1234.6

console.log(precisionRound(1234.5678, -1));
// expected output: 1230

var inp=document.querySelectorAll('input');var btn=document.querySelector('按钮');btn.onclick=函数(){inp[2].value=precisionRound(parseFloat(inp[0].value)*parseFlat(inp[1].value,5);};//MDN功能函数精度Round(数字,精度){var因子=数学功率(10,精度);return数学舍入(number*factor)/factor;}按钮{显示:块;}<输入类型=“文本”值=“0.1”><输入类型=“文本”值=“0.2”><button>获取产品</button><input-type='text'>

更新日期:2019年8月20日

刚刚注意到这个错误。我认为这是由于Math.rround()的浮点精度错误造成的。

precisionRound(1.005, 2) // produces 1, incorrect, should be 1.01

这些条件正常工作:

precisionRound(0.005, 2) // produces 0.01
precisionRound(1.0005, 3) // produces 1.001
precisionRound(1234.5, 0) // produces 1235
precisionRound(1234.5, -1) // produces 1230

Fix:

function precisionRoundMod(number, precision) {
  var factor = Math.pow(10, precision);
  var n = precision < 0 ? number : 0.01 / factor + number;
  return Math.round( n * factor) / factor;
}

这只是在舍入小数时向右加一个数字。MDN已经更新了Math.rround()页面,所以也许有人可以提供更好的解决方案。


如果不想每次都调用函数,可以创建一个类来处理转换。

class Decimal {
  constructor(value = 0, scale = 4) {
    this.intervalValue = value;
    this.scale = scale;
  }

  get value() {
    return this.intervalValue;
  }

  set value(value) {
    this.intervalValue = Decimal.toDecimal(value, this.scale);
  }

  static toDecimal(val, scale) {
    const factor = 10 ** scale;
    return Math.round(val * factor) / factor;
  }
}

用法:

const d = new Decimal(0, 4);
d.value = 0.1 + 0.2;              // 0.3
d.value = 0.3 - 0.2;              // 0.1
d.value = 0.1 + 0.2 - 0.3;        // 0
d.value = 5.551115123125783e-17;  // 0
d.value = 1 / 9;                  // 0.1111

当然,在处理十进制时,需要注意:

d.value = 1/3 + 1/3 + 1/3;   // 1
d.value -= 1/3;              // 0.6667
d.value -= 1/3;              // 0.3334
d.value -= 1/3;              // 0.0001

理想情况下,您希望使用高比例(如12),然后在需要呈现或存储时将其向下转换。就我个人而言,我曾尝试创建一个UInt8Array并试图创建一个精度值(很像SQL Decimal类型),但由于Javascript不允许重载运算符,所以如果不能使用基本的数学运算符(+,-,/,*),而是使用add()、substract()、mult()等函数,就会有点乏味。为了我的需要,这不值得。

但如果您确实需要这种精度,并且愿意忍受在数学中使用函数,那么我推荐decimal.js库。


decimal.js、big.js或bignumber.js可用于避免Javascript中的浮点操作问题:

0.1 * 0.2                                // 0.020000000000000004
x = new Decimal(0.1)
y = x.times(0.2)                          // '0.2'
x.times(0.2).equals(0.2)                  // true

big.js:极简主义;易于使用;以小数位数表示的精度;精度仅适用于除法。

bignumber.js:基数2-64;配置选项;NaN;无穷以小数位数表示的精度;精度仅适用于除法;基本前缀。

decimal.js:基数2-64;配置选项;NaN;无穷非整数幂,exp,ln,log;以有效数字表示的精度;始终应用精度;随机数。

链接到详细比较


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

让我们以一种优雅的、可重用的方式来处理这个问题。通过在数字、公式或内置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

干杯


避免在使用整数的操作过程中处理浮点

正如迄今为止投票最多的答案所述,你可以使用整数,这意味着你所使用的每一个小数乘以10,然后将结果除以所使用的相同数字。

例如,如果使用2个小数,则在执行运算之前,将所有因子乘以100,然后将结果除以100。

下面是一个示例,Result1是通常的结果,Result2使用解决方案:

var Factor1=“1110.7”;var Factor2=“2220.2”;var Result1=数字(因子1)+数字(因子2);var Result2=((数量(因子1)*100)+(数量(系数2)*100))/100;var Result3=(Number(parseFloat(Number(Factor1))+parseFlat(Number(Factor2))).toPecision(2));document.write(“Result1:”+Result1+“<br>Result2:”+Result2+“<br>Result3:”+Result3);

第三个结果是显示改用parseFloat时发生的情况,这在我们的案例中产生了冲突。


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

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)

我正在寻找相同的修复程序,我发现如果您在1中添加一个整数并计算console.log(0.1*0.2+1);。结果是1.02。这可用于将原始x变量舍入为正确的值。

在您的示例中检索到小数位数2的长度后,我们可以将其与toFixed()函数一起使用,以正确舍入原始的x变量。

在代码内部查看此函数在注释部分中的作用。

var myX=0.2*0.1;var myX=42.5-42.65;var myX=123+333+33.33+3333.3+333+333;console.log(myX);//输出(示例1):0.020000000000000004//输出(示例2):-0.149999999999999858//输出(示例3):34458.630000000005//错误函数fixRoundingError(x){// 1. 舍入到最接近的10//在其他一些情况下,还将值的舍入值加1,稍后将使用原始的x变量来获得校正的结果。var xRound=eval(x.toFixed(10))+1;// 2. 使用正则表达式,删除所有数字,直到计算出修正方程的小数位数。。var xDec=xRound.toString().replace(/\d+\.+/gm,“”);// 3. 获取小数位数的长度。var xDecLen=xDeclength;// 4. 使用原始x变量和十进制长度来解决舍入问题。var x=eval(x).toFixed(xDecLen);// 5. 计算新的x变量,以删除任何不需要的尾随0(如果有)。返回eval(x);}console.log(fixRoundingError(myX));//输出(示例1):0.02//输出(示例2):-0.15//输出(示例3):34458.63//正确

在我尝试过的每一种情况下,它都会返回与窗口中计算器相同的值,如果有任何尾随的0,也会自动循环结果。


从我的角度来看,这里的想法是将fp数舍入,以便获得一个漂亮/简短的默认字符串表示。

53位有效位精度提供15到17位有效小数位数精度(2−53≈1.11×10−16)。如果具有最多15个有效数字的十进制字符串被转换为IEEE 754双精度表示,然后转换回具有相同位数的十进制字符串,最终结果应与原始字符串匹配。如果IEEE 754双精度数字被转换为具有至少17个有效数字的十进制字符串,然后转换回双精度表示,最终结果必须与原始数字匹配。...由于分数(F)有效位的52位出现在内存格式中,因此总精度为53位(约16位小数,53 log10(2)≈15.955)。。。维基百科

(0.1).toPrecision(100) ->
0.1000000000000000055511151231257827021181583404541015625000000000000000000000000000000000000000000000

(0.1+0.2).toPrecision(100) ->
0.3000000000000000444089209850062616169452667236328125000000000000000000000000000000000000000000000000

然后,据我所知,我们可以将值四舍五入到15位,以保持良好的字符串表示。

10**Math.floor(53 * Math.log10(2)) // 1e15

eg.

Math.round((0.2+0.1) * 1e15 ) / 1e15
0.3
(Math.round((0.2+0.1) * 1e15 ) / 1e15).toPrecision(100)
0.2999999999999999888977697537484345957636833190917968750000000000000000000000000000000000000000000000

功能如下:

function roundNumberToHaveANiceDefaultStringRepresentation(num) {

    const integerDigits = Math.floor(Math.log10(Math.abs(num))+1);
    const mult = 10**(15-integerDigits); // also consider integer digits
    return Math.round(num * mult) / mult;
}

根据@SheetJs的回答,将其组合在一起,我很喜欢:

getCorrection Factor(numberToCheck:number):数字{var correction Factor:数量=1;if(!Number.isInteger(numberToCheck)){while(!Number.isInteger(numberToCheck)){校正系数*=10;numberToCheck*=校正系数;}}回归修正因子;}


我喜欢带有修正系数的方法,这里是我对ES6和ES5标准的简短决定。与toFixed方法相比,它的优势在于,如果我们想舍入到数百,它不会在数字末尾留下不必要的零,但结果数字是第十个数字:

ES6变体:

// .1 + .2
((a,b,crr) => (a*crr + b*crr)/crr)(.1,.2,100/*correction factor*/);//0.3
// .1 * .2
((a,b,crr) => a*crr*b/crr)(.1,.2,100);//0.02

ES5变体:

// .1 + .2
(function(a,b,crr){ return (a*crr + b*crr)/crr; })(.1,.2,100/*correction factor*/);//0.3
// .1 * .2
(function(a,b,crr){ return a*crr*b/crr; })(.1,.2,100);//0.02

我通常用这样的东西。

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

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

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


我找不到使用内置的Number.EPSILON来解决这类问题的解决方案,因此我的解决方案如下:

function round(value, precision) {
  const power = Math.pow(10, precision)
  return Math.round((value*power)+(Number.EPSILON*power)) / power
}

这使用1和大于1的最小浮点数之间的已知最小差值来修正EPSILON舍入误差,结果仅比舍入阈值低一个EPSILON。

64位浮点的最大精度为15,32位浮点的最高精度为6。您的javascript可能是64位。


如果需要进行任意精度浮点计算,可以使用我的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>


我的回答可能会迟到,但我的解决方案是:

函数浮点(方程式,精度=9){return Math.floor(公式*(10**精度))/(10**精确度);}console.log(浮点(0.1*0.2));//=>0.02console.log(浮点(0.2+0.4));//=>0.6console.log(浮点(1/3));//=>0.333333333console.log(浮点(1/3,2));//=>0.33


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


浮点值编程缺乏精度

在不同的编程语言中,浮点值编程缺乏精度是一个已知的问题。Javascript是使用浮点值进行数学运算时遇到问题的一种。这种问题主要是由于这些编程语言实现的浮点数的内存二进制表示。

让我们希望编程语言、编译器、硬件开发人员和工程师能够一次性解决这些问题,至少解决1024个浮点数字。。。也许这将足以让99.999%的其他开发人员在下一个世纪的编程中感到安心。。。

一种可能的解决方案:转换为整数,计算,然后返回

一种可能的解决方案是将浮点数转换为整数,执行计算,然后将结果转换回浮点数。这似乎在大多数时候都有效。原则上:

函数测试(){变量x=0.1*0.2;//原始方法变量y=((0.1*10)*(0.2*10))/100;//可行的解决方案document.write(x+'<br>'+y);}测试();

当然,在需要的情况下,可以相应地调整功能,例如:

函数测试(){变量x=0.12*0.23;//原始方法变量y=((0.12*100)*(0.23*100))/10000;//可行的解决方案document.write(x+'<br>'+y);}测试();

它也可以是自动化的:

函数myToInt(v){设i=1;设r;而(r%1!=0){i=i*10;r=v*i;}返回[r,i];}函数测试(){设x=0.11111111;设y=0.22222222;设a=x*y;//原始方法[x,i]=myToInt(x);[y,j]=myToInt(y);设d=i*j;设b=x*y/d;//另一种可行的解决方案console.log(a+'\n'+b)document.write(a+'<br>'+b);}测试();

考虑到这一点,我们可以这样做:

函数myToInt(v){设i=1;设r;而(r%1!=0){i=i*10;r=v*i;}返回[r,i];}函数测试(){设x=14898设y=10.8设z=100设a=x*y/z;//原始方法[y,i]=myToInt(y);设b=(x*y)/(z*i);//另一种可行的解决方案console.log(a+'\n'+b)document.write(a+'<br>'+b);}测试();