将四舍五入的十进制百分比相加等于1(或100%)

时间:2020-07-31 20:48:49

标签: sql oracle sum rounding window-functions

在这种情况下,我需要计算一系列数字余额的百分比,四舍五入到小数点后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 ()

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

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的总和?

3 个答案:

答案 0 :(得分:2)

没有完美的方法可以解决此问题。从本质上讲,取整值确实会影响全局计算,此后您可以做的一切都只是围绕基本的数学规则进行。

我想到的其中一项肮脏的工作是计算舍入后的值的连续和,并调整其中一个值的总和,使之匹配。例如,我们可以调整最大的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

当然,还有其他选择(例如计算总体失配,然后尝试将其大致平均地分布在各行组中),但无论如何都不干净。

答案 1 :(得分: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来写,但是由于我也不能在同一个查询中对它进行分析求和,因此我更喜欢直接计算“报告比率”。

答案 2 :(得分:1)

如果我没记错的话,问题就出在值被除法残缺的情况下。一个示例:三行,每行值为10。每行代表总数的1000/3%或33.‾3%。 (对不起,我还没有找到一种方法可以使三键合剂归位,属于三位。)如果将其四舍五入为任意数字,最终得到的总和将小于原始总和。例如。 33.3 + 33.3 + 33.3 = 99.9,而33.‾3 + 33.‾3 + 33.‾3 = 100。

因此,您不能将单个百分比四舍五入到n位数字,而仍然可以获得100的保证金。您将不得不伪造一些数字,或者显示分数。