【问题标题】:Calculate average of two numbers with the same precision计算具有相同精度的两个数字的平均值
【发布时间】:2013-01-30 16:05:50
【问题描述】:

我需要在 JavaScript 中计算两个实数之间的中点(平均值)。数字的范围可以变化很大,一般在 10000 到 0.0001 之间。

天真的方法

(parseFloat(first) + parseFloat(second)) / 2

给我不需要的精度错误,即

(1.1 + 0.1) / 2 = 0.6000000000000001

如何确保结果没有多余的小数位?我想,由于只有两个输入,因此结果最多需要比输入多一位小数。所以,我需要:

 1000 and 3000 to return 2000 (without decimal spaces)
 1234.5678 and 2468.2468 to return 1851.4073
 0.001 and 0.0001 to return 0.00055
 10000 and 0.0001 to return 5000.00005
 0.1 and 1.1 to return 0.6

澄清一下:我完全了解精度误差以及为什么会发生这种情况。我需要的是一个简单的解决方法,但我无法在 SO 上找到以前的解决方案。

【问题讨论】:

  • 您需要 1) 阅读一些关于计算中数字的基础知识(主要是 IEEE754)2) 在显示时格式化您的数字。
  • 或者甚至使用谷歌,或Stack Overflow 自己的搜索来找到无数关于为什么会发生这种情况的问题。我很惊讶您没有使用这种具有如此高声誉的方法。我建议尝试一件事:alert(parseFloat("0.1")); 你会感到惊讶...
  • @dystroy 了解规范并不能真正帮助我
  • @SWeko 是的,它会...尝试将 0.6 写为二进制数。然后你会看到会发生什么。在 Java 中,这就是我们使用 BigDecimal 等的原因。这是good question about this。特别是 toFixed 很有趣
  • @ppeterka 好的,让我换个说法:Javascript 的 BigDecimal 在哪里?

标签: javascript average


【解决方案1】:

您需要使用函数toFixed()。这是您的代码可能看起来像((1.1 + 0.1) / 2).toFixed(4);。 4 是小数位数。

这是一个示例

var num1 = "123.456";
var num2 = "456.1235";
if (num1.split('.')[1].length > num2.split('.')[1].length)
  var x = ((parseFloat(num1) + parseFloat(num2)) / 2).toFixed(num1.split('.')[1].length+1);
else
  var x = ((parseFloat(num1) + parseFloat(num2)) / 2).toFixed(num2.split('.')[1].length+1);

记住输入是字符串,所以num1num2 是字符串。

更新:这是您想要的结果的正确 if 语句。

var num1 = "123.456";
var num2 = "456.1235";
if (num1.split('.').length == 1) {
  if (num2.split('.').length == 1)
    var x = ((parseFloat(num1) + parseFloat(num2)) / 2).toFixed(0);
  else
    var x = ((parseFloat(num1) + parseFloat(num2)) / 2).toFixed(num2.split('.')[1].length);
} else if (num2.split('.').length == 1)
  var x = ((parseFloat(num1) + parseFloat(num2)) / 2).toFixed(num2.split('.')[1].length);
else if (num1.split('.')[1].length == num2.split('.')[1].length)
  var x = ((parseFloat(num1) + parseFloat(num2)) / 2).toFixed(num2.split('.')[1].length);
else if (num1.split('.')[1].length > num2.split('.')[1].length)
  var x = ((parseFloat(num1) + parseFloat(num2)) / 2).toFixed(num1.split('.')[1].length+1);
else if (num1.split('.')[1].length < num2.split('.')[1].length)
  var x = ((parseFloat(num1) + parseFloat(num2)) / 2).toFixed(num2.split('.')[1].length+1);

编辑我不得不更改它检查没有小数空格的数字的顺序。 这是我为帮助测试而创建的一个 jsFiddle:link

【讨论】:

  • 对我不起作用,因为我可能不需要小数位,或者超过 4 个小数位。将尝试去掉尾随的零,看看效果如何。
  • 您可以轻松地添加到 if 语句以获得您想要的结果。
  • 实际上这样的事情可能会奏效。现在我不确定1.0010.999 的平均值应该显示为:)
  • 我添加了额外的 if 语句来达到你想要的结果:)
【解决方案2】:

Javascript 中的数字有 toFixed(n) 方法,它可以删除除 n 位小数之外的所有数字。但是,当您不知道您的数字是非常大还是非常小时,这并不是很有帮助。但是你可以做的是首先对数的 Math.floor() 以数字的 10 为底得到点前面的小数位数,将数字除以 10^places,执行toFixed(n) 其中 n 是最大位数,然后再乘以 10^places。

【讨论】:

  • 这听起来很合理,忘记了对数。有效果就试试。
【解决方案3】:

javascript 中的数字都是IEEE754 定义的双精度浮点数。这意味着没有以 10 为底定义的点位置,并且您总是会发现精度似乎不是您想要的那些情况。

解决办法是

  • 不要使用 js 数字进行计算。这种解决方案很繁重,通常没用。
  • 在需要显示数字时(并且仅在那时)将数字格式化为字符串。您可以使用to Fixed or toPrecision

有用的阅读:What Every Computer Scientist Should Know About Floating-Point Arithmetic

【讨论】:

    【解决方案4】:

    您可以使用所使用数字的“精度”来判断返回值的适当精度。

    此示例将任意数量的数字平均到其参数的最大精度。

    Math.average= function(){
        var a= arguments, L= a.length, i= 0,
        total= 0, next, prec= [];
        if(L== 1) return +a[0];
        while(i<L){
            next= Number(a[i++]);
            total+= next;
            if(next!== Math.floor(next)){
                prec.push(String(next).split('.')[1].length);
            }
        }
        prec= Math.max.apply(Math, prec)+1;
        return +((total/L).toFixed(prec));
        // returns number after precision adjusted
    }
    

    /*

    tests:
    var n1= ;
    Math.average(1.1, .01);
    0.555
    
    var n1= 1.0111, n2= .01;
    Math.average(1.0111,  .01);
    0.51055
    
    var n1= 1.1, n2= .01, n3= 1, n4= 1.025;
    Math.average(n1, n2, n3, n4)
    0.7838
    

    */

    【讨论】:

      【解决方案5】:

      您可以使用Number.toPrecision(digits) 来执行此操作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-04-20
        • 1970-01-01
        • 1970-01-01
        • 2022-01-08
        • 2019-01-28
        • 2022-08-17
        • 1970-01-01
        相关资源
        最近更新 更多