【问题标题】:Correct sums with dividing sums, countering rounding errors用除法校正总和,消除舍入误差
【发布时间】:2012-07-23 11:56:21
【问题描述】:

使用 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.34 和 3.32,而最佳分割将是 3.34、3.34、3.33、3.33。

有些人可能会争辩说,足够的小数只有在有大量行时才会出现问题。但在一个经济的系统中,我认为从一开始就拥有一个故障安全系统是很重要的。它需要可扩展,不能有丝毫错误。 不公平没关系,只是没有错误。

类似问题:sum divided values problem (dealing with rounding error)

【问题讨论】:

  • " 不公平是可以的,只是没有错误。" - 喜欢它
  • 我想我会找到两个最接近的分割值,然后在分配它们时在两者之间切换。然后弄清楚如何处理(潜在的)最后一个奇数值。
  • 这就是每个问题的外观:)。很好的描述。来自我的 +1。
  • 在涉及金钱的情况下,我认为公平最终总是很重要——如果您的系统一直对某一类用户收取过高的费用(例如基于 ID 号、注册日期、按字母顺序排列的姓名,或者您的数据库)排序记录),有一些集体诉讼的可能性。一种公平的方法是,每次计算收费金额时,从尚未收费的用户列表中随机选择一个用户进行收费。

标签: php divide rounding-error


【解决方案1】:

这似乎有效 - 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/

【讨论】:

  • 非常感谢!不过有几个问题:1. 将 $payment 转换为 int 时,会导致 3.33 还是 3.34,即。圆的还是不圆的? 2. 在您精彩的 js 脚本中尝试不同的值时,我可以得到未四舍五入到小数点后两位的值。我应该在 if 语句的某处进行类似的 int-cast 吗?
  • 1) 结果是 3.33。 2)你能给我一些例子吗?
  • 例如,如果您将价格设置为 13.37。
  • 更新的示例应该可以解决它。
  • 是的,但是打破了一些新的东西。 17.42 不等于 17.42。正在尝试定位错误。
【解决方案2】:

也许为每个用户建立一个新的“四舍五入平衡”。如果是 10 分三路,那么每个用户将支付 3.34。每个用户的四舍五入余额也将有 2/3 美分。 (这必须存储为我认为的文本)。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-29
    相关资源
    最近更新 更多