无尾差计算百分比
目录
4 个项目合成一个总数
然后得到这 4 个项目占总数的百分比,并展示
业务常见类似下面这样
tsx
const total = list.reduce((acc, cur) => acc + cur.value, 0);
/// ....
return (
<div>
{list.map((item) => {
return (
<div>
<div>{item.name}</div>
<div>{item.value}</div>
<div>{((item.value / total) * 100).toFixed(2)}</div>
</div>
);
})}
</div>
);
但是存在尾差问题,即四个项目的百分比相加不等于 100%
已知百分比获取的时候是四舍五入取 2 位小数(由 toFixed()实现)
列出数据
> (282/25995*100).toFixed(6)
'1.084824'
> (1357/25995*100).toFixed(6)
'5.220235'
> (963/25995*100).toFixed(6)
'3.704559'
> (23393/25995*100).toFixed(6)
'89.990383'
发现没有一个数据会触发“五入”(小数点后第三位都<5)导致出现尾差
小数点 3 位以及之后的值加起来应该 = 0.01 也就是差去的数
对于这些百分比的小数后第三为 ,存在 3 种情况
- 其中一个>5,产生进位,那么尾差就会被弥补,不用担心
- 对于所有都>5,无法产生进位,导致尾差产生,需要考虑
- 存在多个>5,产生过多的进位
最合理的方式是对小数点 3 位以及之后的值最大的百分比进位,但是实现起来比较麻烦
目前的解决方案是,最后一个值不由 item.value / total
而是 100-前面所有百分比的和
当然也可以在外第一一个值,逐个比较,记录最大的值,然后进位,但是不够优雅
tsx
const total = list.reduce((acc, cur) => acc + cur.value, 0);
/// ....
<div>
{list.map((item) => {
return (
<div>
<div>{item.name}</div>
<div>{item.value}</div>
<div>{((item.value / total) * 100).toFixed(2)}</div>
</div>
);
})}
</div>;
相对优雅的方案
tsx
const total = list.reduce((acc, cur) => acc + cur.value, 0);
const percent_max = list.reduce((acc, item, index) => {
if (acc == -1) return acc;
const percent = ((item.value / total) * 100000) % 10;
if (percent >= 5) {
return -1;
} else {
const percent2 = ((list[acc].value / total) * 100000) % 10;
return percent > percent2 ? index : acc;
}
}, 0);
/// ....
<div>
{list.map((item) => {
const pec = (
(item.value / total) * 100 +
(index == percent_max ? 0.01 : 0)
).toFixed(2);
return (
<div>
<div>{item.name}</div>
<div>{item.value}</div>
<div>{pec}</div>
</div>
);
})}
</div>;
但是这样无法解决 3 的问题,今天脑子用完了,下次再说