Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/467.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何处理JavaScript中的浮点数精度?_Javascript_Floating Point - Fatal编程技术网

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

如何处理JavaScript中的浮点数精度?,javascript,floating-point,Javascript,Floating Point,我有以下虚拟测试脚本: 功能测试(){ var x=0.1*0.2; 文件。编写(x); } test()看一看。如果您想要操作的数字范围很小(例如,货币),那么它可能会解决您的问题。我会将它四舍五入到几个十进制值,这是最简单的解决方案 使用 var x = 0.1*0.2; x =Math.round(x*Math.pow(10,2))/Math.pow(10,2); 试试我的智利算术库,你可以看到。 如果您想要一个更高版本,我可以给您一个。您不能用二进制浮点类型(ECMAScript使

我有以下虚拟测试脚本:

功能测试(){
var x=0.1*0.2;
文件。编写(x);
}
test()看一看。如果您想要操作的数字范围很小(例如,货币),那么它可能会解决您的问题。我会将它四舍五入到几个十进制值,这是最简单的解决方案

使用

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

试试我的智利算术库,你可以看到。
如果您想要一个更高版本,我可以给您一个。

您不能用二进制浮点类型(ECMAScript使用二进制浮点类型来表示浮点值)精确地表示大多数小数。因此,除非您使用任意精度的算术类型或基于十进制的浮点类型,否则没有一个优雅的解决方案。例如,.

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

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

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

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


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

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

试试看,你会这样称呼它:

var yourString = sprintf("%.2f", yourNumber);
将数字打印为小数点后两位的浮点数


如果您不想包含更多的文件,而只是为了将浮点舍入到给定的精度,那么也可以将其用于显示目的

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

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

---或---

---也---

---如---


此函数将从两个浮点数的乘法中确定所需的精度,并返回具有适当精度的结果。虽然不是很优雅

function multFloats(a,b){
  var atens = Math.pow(10,String(a).length - String(a).indexOf('.') - 1), 
      btens = Math.pow(10,String(b).length - String(b).indexOf('.') - 1); 
  return (a * atens) * (b * btens) / (atens * btens); 
}

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

二进制浮点中的0.1就像十进制中的1/3(即0.3333…永远),只是没有精确的方法来处理它

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

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


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

要避免这种情况,您应该使用整数值而不是浮点。所以,当你想要2个位置的精度值为*100时,对于3个位置,使用1000。显示时,使用格式化程序放入分隔符

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

来自:

如何避免此问题?

这取决于什么样的环境 你正在做的计算

  • 如果你真的需要把结果加起来,特别是当你 与钱打交道:使用特殊的小数点 数据类型
  • 如果你只是不想看到那些额外的小数位:简单地说 将结果四舍五入为固定格式 输入时的小数位数 展示它
  • 如果没有可用的十进制数据类型,另一种方法是工作 使用整数,例如domoney 计算完全以美分为单位。但是 这是更多的工作,并有一些 缺点
请注意,第一点仅适用于确实需要特定精确小数行为的情况。大多数人
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
function multFloats(a,b){
  var atens = Math.pow(10,String(a).length - String(a).indexOf('.') - 1), 
      btens = Math.pow(10,String(b).length - String(b).indexOf('.') - 1); 
  return (a * atens) * (b * btens) / (atens * btens); 
}
function strip(number) {
    return (parseFloat(number).toPrecision(12));
}
num = .01 + .06;  // yields 0.0699999999999
rnum = round(num,12); // yields 0.07
> 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
function test(){
    var x = 0.1 * 0.2;
    document.write(Number(x).toFixed(2));
}
test();
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);
function dec( num )
{
    var p = 100;
    return Math.round( num * p ) / p;
}
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;
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
function precisionRound(number, precision) {
  var factor = Math.pow(10, precision);
  return Math.round(number * factor) / factor;
}
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
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}`;
}

(0.1).toPrecision(100) ->
0.1000000000000000055511151231257827021181583404541015625000000000000000000000000000000000000000000000

(0.1+0.2).toPrecision(100) ->
0.3000000000000000444089209850062616169452667236328125000000000000000000000000000000000000000000000000
10**Math.floor(53 * Math.log10(2)) // 1e15
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;
}