rizka
(rizka)
2016 年11 月 1 日 18:39
1
续接 投票中百分比的四舍五入不准确 的讨论:
我决定开一个新话题,因为最新的问题并非关于有争议的舍入规则,而是完全错误的规则。因此,这个话题必须归类到 Contribute > Bug 类别。虽然不是什么大问题,但确实有些尴尬。
我们进行了一次公开投票,共有 106 票。其中 69 票投给“否”,37 票投给“是”。简单的除法计算显示,69/106 约为 65.09%。然而,系统却显示 66% 投给“否”,34% 投给“是”。请看图片:
最终结果是 72 票投给“否”,38 票投给“是”,投票现已关闭。72/110 约为 65.45%,系统也将其舍入为 66%,因此你可以在 Tappara.co 看到这一 bug 的实际表现。
我试图查看代码以找出导致 bug 的原因,但未能成功。我发现有一个名为 evenRound 的函数被调用,但当我搜索其源码时,没有找到。这只是一个猜测,也许该函数是将数字舍入到最近的 偶数 整数?
Not sure I understand what’s broken here. The poll will round the % so they add up to 100%.
rizka:
I tried to look at the code to find what causes the bug, but failed. I discovered that a function named evenRound is called, but when I seeked for its source, I didn’t find it.
// stolen from http://stackoverflow.com/a/13484088/11983
function sumsUpTo100(percentages) {
return percentages.map(p => Math.floor(p)).reduce((a, b) => a + b) === 100;
}
export default (percentages) => {
const sumOfDecimals = Math.ceil(percentages.map(a => a % 1).reduce((a, b) => a + b));
// compensate error by adding 1 to the first n "non-zero" items
for (let i = 0, max = percentages.length; i < sumOfDecimals && i < max; i++) {
if (percentages[i] > 0) {
percentages[i] = ++percentages[i];
// quit early when there is a rounding issue
if (sumsUpTo100(percentages)) break;
}
}
return percentages.map(p => Math.floor(p));
};
1 个赞
rizka
(rizka)
2016 年11 月 2 日 12:45
3
结果很糟糕,问题就出在这里。
谢谢你的代码。我只是没找到它,这说明我在用 GitHub 方面真的很菜。代码是从 Stack Overflow 上摘录的,原作者写道:
我不确定你需要多高的精度,但我建议的做法是:将小数部分总和向上取整得到的数值 n,对前 n 个数各加 1,其余的向下取整。比如这里 n 是 3,那么我就给前 3 项加 1,其余项向下取整。当然,这种方法不是超级精确,有些数字可能会在不该四舍五入的时候被舍入,但它效果还不错,而且最终结果总是 100%。
用这么粗暴的方法得到奇怪的结果也不足为奇。我建议我们要稍微复杂一点。这篇帖子 在同一个话题下获得了 72 个赞成票,而代码来源的那篇帖子只有 1 个。
如果你不介意依赖原始的小数数据,有很多方法可以做到这一点。
第一种,也许也是最流行的方法是最大余数法(Largest Remainder Method)
其基本步骤是:
将所有数字向下取整
计算总和与 100 之间的差值
按照小数部分从大到小的顺序,将差值分配给各项(每项加 1)
…
我认为这种方法是最好的选择。它仍然非常简单,而且结果要好得多。当前的代码需要增加几行,以便告诉函数哪些数字应该向上取整。不过,还有一个问题需要考虑。来自同一话题的 这篇帖子 指出:
Varun Vohra 的高赞答案最小化了绝对误差之和,而且实现起来非常简单。但是,它无法处理一些边缘情况——比如对 24.25、23.25、27.25、25.25 进行四舍五入时,结果应该是什么?其中有一个数字需要向上取整而不是向下取整。
该帖子提出了各种方法来选择哪些数字向上取整,哪些向下取整,但你永远无法完全避免任意性。如果你追求完美,可以进一步阅读该帖子,但在这些罕见的特殊情况下,我也满足于使用这种方法。
你大概会任意选择列表中的第一个或最后一个。
希望这能帮到你。如果我先学习一些基本语法,我的技能可能足以自己进行改进。我现在不打算这么做,但如果有人在此之前没有处理这个问题,也许以后会做。
编辑:我把标题中的“broken”改成了“imprecise”,稍微优化了一下标题。这个话题也应该移到 Contribute > Feature 频道,或者与之前的话题合并。
4 个赞
Feel free to submit a pull request to change the rounding algorithm
5 个赞
rizka
(rizka)
2016 年11 月 3 日 09:22
5
It was like classic exercises for beginning programmers. Here is a way to do it, but someone who has more experience and isn’t totally new to JavaScript might find some shortcuts.
https://github.com/rizka10/discourse/pull/1/files
2 个赞
Looks fine to me. Would you mind making it a PR so that I can merge it?
rizka
(rizka)
2016 年11 月 3 日 09:27
7
I thought that was a pull request already… I’m as confused as always with Github.
I clicked some more green buttons now, maybe it does the trick?
rizka
(rizka)
2016 年11 月 3 日 09:36
9
I think I’m getting it now. I ran into some Github tutorials online yesterday and I should really go through one to learn the basics.
1 个赞
Awesome. Before we can merge the code, we’ll need you to sign the CLA. I promise, this is the last step (also, you only have to sign it once ;))
6 个赞
rizka
(rizka)
2016 年11 月 3 日 09:40
11
Thanks for all the help! I signed it now.
4 个赞