1

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

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

这给了我非常好的实际结果。但是,在我的单元测试中,我无法断言事情是否正常工作,因为我一直得到显着的舍入错误。

在三个测试用例中,我尝试获取第 40 、50和60百分位数,从而得出以下计算结果:

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
}

这使我的断言失败,它正在寻找1.2第 40百分位数。

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

4

4 回答 4

1

chai closeTo 旨在处理此类测试:

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

第一个参数是期望值,第二个参数是指示计算值需要多接近 1.2 的增量。

于 2013-10-03T03:07:41.060 回答
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.
于 2013-09-22T04:49:26.690 回答
0

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

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

即使您使用精确算术评估插值,我们也可以通过将 asTrueFraction 替换为 asMinimalDecimalFraction 来做到这一点(这会为您提供具有最小位数的十进制数,将四舍五入为相同的浮点数):

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 是最佳值。

我并不是说插值公式在编写时总是准确的,它可以累积舍入误差,但它已经对您的输入数据执行了不错的工作。

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

于 2013-09-23T10:32:51.227 回答
0

我看到了两种解决该问题的方法:

  1. 如果结果中只有有限的数字,您可以将数字四舍五入到一些小的精度

  2. 分裂让你感到困惑。将等式的两边乘以除法器,结果中将不再有无限小数:)

于 2013-09-22T04:48:25.210 回答