有没有办法提高这个表达式的浮点精度?

时间:2013-09-22 04:39:04

标签: javascript floating-point floating-accuracy floating-point-precision chai

我有一个表达式,用于通过在两个值之间插值来估计百分位数。

windowMin + (currentPercentile - lastPercentile) * (windowMax - windowMin) / (percentile - lastPercentile)

这给了我非常好的现实结果。但是,在我的单元测试中,我遇到麻烦,事情正常,因为我一直都会出现明显的舍入错误。

在三个测试案例中,我尝试获得40 th ,50 th 和60 th 百分位数,从而导致这些计算:< / p>

1 + (0.4 - 0.3333333333333333) * (2 - 1) / (0.6666666666666666 - 0.3333333333333333)
1 + (0.5 - 0.3333333333333333) * (2 - 1) / (0.6666666666666666 - 0.3333333333333333)
1 + (0.6 - 0.3333333333333333) * (2 - 1) / (0.6666666666666666 - 0.3333333333333333)

这会产生:

{
  "0.4": 1.2000000000000002,
  "0.5": 1.5,
  "0.6": 1.8
}

这使我的断言失败,正在为40 th 百分位数寻找1.2

有没有办法重构这个表达式以提高所有情况下的准确性?如果没有,是否有一种简单的方法可以解决chai断言这个问题?

4 个答案:

答案 0 :(得分:1)

这些舍入误差是浮点数学的一个特征。

一种可能的解决方案可能是在返回结果之前将.toPrecision()应用于您的计算:

var result = windowMin + (currentPercentile - lastPercentile) * (windowMax - windowMin) / (percentile - lastPercentile);
return result.toPrecision(6);  // returns six significant figures

或可能toFixed()

return result.toFixed(2); // returns two decimal places.

答案 1 :(得分:1)

chai closeTo旨在处理这种测试:

expect(calculatedValue).to.be.closeTo(1.2, 0.000001);

第一个参数是预期值,第二个参数是一个delta,表示calculateValue需要与1.2的接近程度。

答案 2 :(得分:0)

我认为有两种方法可以解决这个问题:

  1. 如果你的结果只有有限的数字,你可以将数字四舍五入到精确度

  2. 分部让您感到困惑。将分布的两侧乘以分频器,结果中你将不再有无限分数:)

答案 3 :(得分:0)

碰巧1.2000000000000002已经是最接近您提交的精确插值的双精度浮点值,如下面的Pharo smalltalk表达式所示(asTrueFraction表示浮点值转换为具有完全相同值的Fraction)

(1 + ((0.4 asTrueFraction - 0.3333333333333333 asTrueFraction) * (2 - 1) / (0.6666666666666666 asTrueFraction - 0.3333333333333333 asTrueFraction))) asFloat
-> 1.2000000000000002.

即使您使用精确算术评估插值,我们也可以通过将asTrueFraction替换为asMinimalDecimalFraction来实现这一点(它将获得带有最小位数的十进制数,并将四舍五入到同一个Float):

0.4 asTrueFraction -> (3602879701896397/9007199254740992).
0.4 asMinimalDecimalFraction -> (2/5).
0.3333333333333333 asMinimalDecimalFraction -> (3333333333333333/10000000000000000).
0.6666666666666666 asMinimalDecimalFraction -> (3333333333333333/5000000000000000).

然后你再次获得相同的结果,看看它是如何分解的:

(1 + ((0.4 asMinimalDecimalFraction - 0.3333333333333333 asMinimalDecimalFraction) * (2 - 1) / (0.6666666666666666 asMinimalDecimalFraction - 0.3333333333333333 asMinimalDecimalFraction))) 
 -> (4000000000000000/3333333333333333).

(4000000000000000/3333333333333333) asFloat ->  1.2000000000000002.

换句话说,如果你想要一个浮点数的结果,那么1.2000000000000002就是最好的值。

我并没有说插值公式总是与写入一样精确,它可以累积四舍五入的错误,但它已经在您的输入数据上执行了不错的工作。

更改测试而不是公式,并插入明确的准确度要求。