添加四舍五入的小数百分比等于 1(或 100%)

Posted

技术标签:

【中文标题】添加四舍五入的小数百分比等于 1(或 100%)【英文标题】:Adding rounded decimal percentages to equal 1 (or 100%) 【发布时间】:2020-07-31 20:48:49 【问题描述】:

在这种情况下,我需要计算一系列数字余额的百分比,四舍五入到小数点后 10 位,但它们的总和必须为 1.0000000000(或 100.0000000000%)。我可以得到 0.9999999998 或 1.0000000004,但我似乎无法达到 1.0。还有什么我可以做的吗?

create table balances (balance number); 

insert into balances (balance) select 27544020.38 from dual; 
insert into balances (balance) select 3161670.46 from dual; 
insert into balances (balance) select 13085937.87 from dual; 
insert into balances (balance) select 0 from dual; 
insert into balances (balance) select 0 from dual; 
insert into balances (balance) select 478033.04 from dual; 
insert into balances (balance) select -85126.17 from dual; 
insert into balances (balance) select 44968439.88 from dual; 
insert into balances (balance) select 78155926.33 from dual; 
insert into balances (balance) select -3662788.36 from dual; 
insert into balances (balance) select 234328177.96 from dual; 
insert into balances (balance) select 103694040.23 from dual; 
insert into balances (balance) select 85295156.11 from dual; 
insert into balances (balance) select 155627180.9 from dual; 
insert into balances (balance) select 133311464.77 from dual; 
insert into balances (balance) select 56306616.42 from dual; 
insert into balances (balance) select 135204546.73 from dual; 
insert into balances (balance) select 188572856.42 from dual; 
insert into balances (balance) select 118208964.69 from dual; 
insert into balances (balance) select 87751901.55 from dual; 
insert into balances (balance) select 947 from dual; 
insert into balances (balance) select 61190729.16 from dual; 
insert into balances (balance) select 35307571.39 from dual; 
insert into balances (balance) select 32229181.69 from dual; 
insert into balances (balance) select 27544020.38 from dual; 
insert into balances (balance) select 3161670.46 from dual; 
insert into balances (balance) select 13085937.87 from dual; 

我正在计算这组余额的百分比:

ratio_to_report(balance) over ()

并将其四舍五入到小数点后 10 位:

round(ratio_to_report(balance) over (), 10) 

当我运行如下查询时:

select balance, ratio_to_report(balance) over (), round(ratio_to_report(balance) over (), 10) as pctg
from balances;

这个结果看起来不错,但是当我通过以下方式检查 sum(pctg) 时:

select sum(pctg) from 
(
   select balance, 
          ratio_to_report(balance) over (),
          round(ratio_to_report(balance) over (), 10) pctg
   from balances
)

我明白了:

      SUM(PCTG) 
------------------ 
   1.0000000004

在添加四舍五入到小数点后 10 位的小数百分比时,是否有其他技术或函数始终得到 1.0000000000 的总和?

【问题讨论】:

这很简单。如果你想得到精确的 1.0,那么不要总结四舍五入的值。 好像和***.com/q/9161404/2527905类似 【参考方案1】:

没有完美的解决方法。从本质上讲,四舍五入确实会影响全局计算,之后您可以做的任何事情都只是围绕基本的数学规则工作。

想到的一项棘手的工作是计算四舍五入值的运行总和,并调整其中一个值以使总数匹配;例如,我们可以调整最大的pctg(因为它可能不太明显)。这并不干净,在极端情况下可能会产生违反直觉甚至错误的结果。

看起来像:

select  
    rtr,
    pctg,
    case when row_number() over(order by pctg desc) = 1
        then 1 - sum(pctg) over(order by pctg) - pctg
        else pctg
    end adujsted_ptcg
from (
   select 
        balance, 
        ratio_to_report(balance) over () rtr,
        round(ratio_to_report(balance) over (), 10) pctg
   from balances
) t
order by pctg desc

当然,还有其他替代方案(例如计算整体不匹配,然后尝试在多组行之间或多或少地平均分配) - 但它们中的任何一个都不是干净的。

【讨论】:

“它们都不是干净的”取决于“干净”的定义。例如,考虑以下优化问题:为每个百分比分配一个带有 10 个精确小数位的“有趣的四舍五入”小数。在“四舍五入”百分比的总和 = 1 的情况下,最小化“错误”的平方和。我相信这是一个完全有效的、“干净”的问题陈述,并且它有一个简单的 SQL 解决方案。示例:如果数字是 1/3、1/3、1/3(并且假设只有三个精确的小数),则该过程将返回 0.333、0.334、0.333。解决方案并不总是唯一的,但确实存在。【参考方案2】:

这是一种方式 - 它在某种意义上是“最佳的”(“最佳”的具体意义是不平凡的,也许不是特别相关)。

with
  prep (balance, rn, p_sum) as (
    select balance, rownum, 
           sum(balance) over (order by rownum) / sum(balance) over ()
    from   balances
  )
select balance, 
       round(p_sum, 10) - round(lag(p_sum, 1, 0) over (order by rn), 10)
         as percentage
from   prep
;

这个想法是跟踪百分比的非四舍五入累积总和;然后将这些累积总和四舍五入;然后将连续四舍五入的累积和的差值作为百分比的“四舍五入值”。

“最佳方式” - 只是不要要求数学证明:如果您考虑所有“四舍五入”百分比的分配,每个非四舍五入的百分比最多十位小数,并且总和必须完全相等1,然后你计算“错误”的平方和(这个有趣的四舍五入的百分比减去确切的百分比),我在这个查询中实现的策略将最小化这个错误的平方和。

这也可以用ratio_to_report 编写,但由于我也无法在同一个查询中对其进行分析求和,因此我更喜欢直接计算“报告比率”。

【讨论】:

【参考方案3】:

如果我没记错的话,问题出在值被除法所破坏。示例:三行,每行的值为 10。每行代表总数的 1000/3% 或 33.‾3%。 (对不起,我还没有找到一种方法来让 vinculum 超过它所属的三个。)如果你将它四舍五入到任意位数,你最终得到的总和小于原始总数。例如。 33.3 + 33.3 + 33.3 = 99.9,而 33.‾3 + 33.‾3+ 33.‾3 = 100.

因此,您不能将单个百分比四舍五入到 n 位,但仍然保证得到 100。您必须要么自愿伪造一些数字,要么显示分数。

【讨论】:

以上是关于添加四舍五入的小数百分比等于 1(或 100%)的主要内容,如果未能解决你的问题,请参考以下文章

vue 保留小数点后两位以及转化为百分比

保留一位小数有几种方式?

请问在JAVA中我用BigDecimal来计算百分比,但是由于不能整除的原因,有些百分比相加不等于100%,该怎么办

java最大余数法(百分比算法Echarts)

取整函数

在C语言中的百分比怎么精确到小数和保留整数四舍五入?