我有一个表达式,用于通过在两个值之间插值来估计百分位数。
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断言这个问题?
答案 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)
我认为有两种方法可以解决这个问题:
如果你的结果只有有限的数字,你可以将数字四舍五入到精确度
分部让您感到困惑。将分布的两侧乘以分频器,结果中你将不再有无限分数:)
答案 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就是最好的值。
我并没有说插值公式总是与写入一样精确,它可以累积四舍五入的错误,但它已经在您的输入数据上执行了不错的工作。
更改测试而不是公式,并插入明确的准确度要求。