考虑下面四个百分比,用浮点数表示:

    13.626332%
    47.989636%
     9.596008%
    28.788024%
   -----------
   100.000000%

我需要用整数表示这些百分比。如果我简单地使用Math.round(),我最终得到的总数是101%。

14 + 48 + 10 + 29 = 101

如果我使用parseInt(),我最终得到了97%。

13 + 47 + 9 + 28 = 97

有什么好的算法可以将任何百分比数表示为整数,同时还保持总数为100%?


编辑:在阅读了一些评论和回答后,显然有很多方法可以解决这个问题。

在我看来,为了保持数字的真实性,“正确”的结果是最小化总体误差的结果,定义为相对于实际值会引入多少误差舍入:

        value  rounded     error               decision
   ----------------------------------------------------
    13.626332       14      2.7%          round up (14)
    47.989636       48      0.0%          round up (48)
     9.596008       10      4.0%    don't round up  (9)
    28.788024       29      2.7%          round up (29)

在平局的情况下(3.33,3.33,3.33)可以做出任意的决定(例如3,4,3)。


当前回答

或者像这样简单,你只需要累积误差…

const p = [13.626332, 47.989636, 9.596008, 28.788024];
const round = (a, e = 0) => a.map(x => (r = Math.round(x + e), e += x - r, r));
console.log(round(p));

结果:[14,48,9,29]

其他回答

如果是四舍五入,就没有办法在所有情况下都得到完全相同的结果。

你可以取你拥有的N个百分比的小数部分(在你给出的例子中是4)。

把小数部分相加。在你的例子中,总分为3。

将分数最高的3个数字上排,其余的取底。

(抱歉修改了)

下面是一个实现了最大余数方法的Ruby宝石: https://github.com/jethroo/lare_round

使用方法:

a =  Array.new(3){ BigDecimal('0.3334') }
# => [#<BigDecimal:887b6c8,'0.3334E0',9(18)>, #<BigDecimal:887b600,'0.3334E0',9(18)>, #<BigDecimal:887b4c0,'0.3334E0',9(18)>]
a = LareRound.round(a,2)
# => [#<BigDecimal:8867330,'0.34E0',9(36)>, #<BigDecimal:8867290,'0.33E0',9(36)>, #<BigDecimal:88671f0,'0.33E0',9(36)>]
a.reduce(:+).to_f
# => 1.0

如果你只有两个选项,你可以使用Math.round()。唯一有问题的值对是X.5(例如;37.5和62.5)它会四舍五入两个值,你最终会得到101%,你可以试试这里:

https://jsfiddle.net/f8np1t0k/2/

因为你需要始终显示100%,你只需从它们中删除一个百分比,例如在第一个

const correctedar= Number.isInteger(around -0.5) ?A - 1: A

或者你可以选择有更多%选票的选项。

1% diff的错误在1-100对值的划分的10k例中发生114次。

您可以尝试跟踪由于舍入而产生的误差,如果累计误差大于当前数字的小数部分,则再反向舍入。

13.62 -> 14 (+.38)
47.98 -> 48 (+.02 (+.40 total))
 9.59 -> 10 (+.41 (+.81 total))
28.78 -> 28 (round down because .81 > .78)
------------
        100

不确定这是否适用于一般情况,但如果顺序相反,似乎也会有类似的效果:

28.78 -> 29 (+.22)
 9.59 ->  9 (-.37; rounded down because .59 > .22)
47.98 -> 48 (-.35)
13.62 -> 14 (+.03)
------------
        100

我相信在某些情况下,这种方法可能会失效,但任何方法都至少在某种程度上是任意的,因为您基本上是在修改输入数据。

或者像这样简单,你只需要累积误差…

const p = [13.626332, 47.989636, 9.596008, 28.788024];
const round = (a, e = 0) => a.map(x => (r = Math.round(x + e), e += x - r, r));
console.log(round(p));

结果:[14,48,9,29]