用除法求和来校正和,以消除舍入误差


Correct sums with dividing sums, countering rounding errors

使用PHP和MySQL数据库编码的Web应用程序

我有一个系统,在分摊成本时,它会为许多人计算不同的成本。例如,人员A以10英镑的价格购买东西,人员B、C和D应分摊成本。

因此,系统应为人员a注册10的阳性记录,为B、C和D注册10/3的阴性记录。

然而,当这样做的时候B、C和D四舍五入后均为-3.33。当然,加起来不等于10。解决这个问题的最佳方法是什么?一个最优的解决方案会随机分配人们得到的东西——也就是稍大的成本。

一个可能的解决方案是,如果我只让最后一个人的债务为10 - (A + B),但如果四个人分摊例如13.34的成本,就会出现问题。然后,不同的部分将是3.34、3.34、3.3 4和3.32,而最佳分割将是3.33、3.34和3.33。

有些人可能会争辩说,有足够的小数,这只是当有大量行时的问题。但在一个经济的系统中,我认为即使从一开始就有一个故障保护系统也是很重要的。它需要具有可伸缩性,并且不能有任何错误不公平是可以的,只是不是错误。

类似的问题:和除法问题(处理舍入误差)

这似乎有效-http://jsfiddle.net/nQakD/。

使用jQuery作为示例,但如果您了解PHP,您应该能够轻松地将其转换为PHP。如果你还需要php代码,告诉我,我会为你写的。

我也会把代码粘贴在这里-

$(document).ready(function() {
    var price = 17.48, people = 4, payment = (price/people).toFixed(2), count=0;
    var payments = [];
    for(i = 0; i < people; i++) {
       payments.push(payment);   
    }
    if(payment*people != price) {
        var currentPayment = payment*people;
        $(payments).each(function() {
            if(currentPayment < price) {
                currentPayment = (currentPayment-this).toFixed(2);
                var newPayment = parseFloat(this)+0.01;
                payments[count] = newPayment.toFixed(2);
                currentPayment = parseFloat(currentPayment)+parseFloat(newPayment);
            }
            else if(currentPayment > price) {
                currentPayment = (currentPayment-this).toFixed(2);
                var newPayment = parseFloat(this)-0.01;
                payments[count] = newPayment.toFixed(2);
                currentPayment = parseFloat(currentPayment)+parseFloat(newPayment);
            }
            count++;
        });   
    }  
    $(payments).each(function() {
        $("#result").append("<b>"+this+"</b><br/>");
    });       
});​

编辑:

这里是工作php代码-

$price = 13.34;
$people = 4;
$payment = (float)$price/$people;
$payment = 0.01 * (int)($payment*100);
$count = 0;
$payments = Array();
for($i = 0; $i < $people; $i++) {
    array_push($payments, $payment);
}
if($payment*$people != $price) {
    $currentPayment = $payment*$people;
    foreach($payments as $pay) {
        if($currentPayment < $price) {
            $currentPayment = $currentPayment-$pay;
            $currentPayment = 0.01 * (int)($currentPayment*100);               
            $newPayment = (float)$pay+0.01;
            $newPayment = 0.01 * (int)($newPayment*100);
            $payments[$count] = $newPayment;
            $currentPayment = (float)$currentPayment+$newPayment;
        }
        else if($currentPayment > $price) {
            $currentPayment = $currentPayment-$pay;
            $currentPayment = 0.01 * (int)($currentPayment*100);               
            $newPayment = (float)$pay-0.01;
            $newPayment = 0.01 * (int)($newPayment*100);
            $payments[$count] = $newPayment;
            $currentPayment = (float)$currentPayment+$newPayment;
        }
        $count++;
    }
}
foreach($payments as $payed) {
    echo '<b>'.$payed.'</b><br />';
}​​​

编辑2:

这应该可以解决js问题-http://jsfiddle.net/nQakD/更新了上面的代码。

编辑3:

编辑了PHP代码和JS代码,使其适用于所有示例-http://jsfiddle.net/nQakD/。

也许可以为每个用户建立一个新的"舍入平衡"。如果是10分成三种方式,那么每个用户将支付3.34。每个用户的四舍五入余额也将有2/3美分。(我想这必须作为文本存储)。

下次发生同样的事情时,请检查舍入平衡。。他们有2/3美分,所以现在你可以减去三分之一(所以他们现在在四舍五入余额中有1/3美分),只收取3.33。