Правильные суммы с разделением сумм, противодействуя ошибкам округления

веб-приложения написанные на PHP с базой данных MySQL.

У меня есть система, которая вычисляет различные затраты для ряда людей при разделении стоимости. Например Человек покупает что-то за 10 и лица B, C и D должны разделить стоимость.

система должна зарегистрировать положительную запись для человек из 10 и отрицательных записей 10/3 для B, C и D.

, когда это будет сделано; 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.

некоторые могут поспорить, что с достаточно десятичных знаков это проблема только при наличии большого количества строк. Но в экономической системе я думаю, что важно иметь отказобезопасности

2 ответов


Это, кажется, работает - 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/>");
    });       
});​

EDIT:

а вот рабочий 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 />';
}​​​

EDIT 2:

Это должно исправить проблему js - http://jsfiddle.net/nQakD/ обновленный код выше.

EDIT 3:

редактировать PHP код и JS код так что он работает для всех примеров - http://jsfiddle.net/nQakD/ .


возможно, сделайте новый "округляющий баланс" для каждого пользователя. Если бы это было 10, чтобы разделить три способа, то каждый пользователь заплатил бы 3,34. Каждый пользователь также будет иметь 2/3 от цента к их округлению баланса. (это должно быть сохранено как текст, я полагаю) .

в следующий раз, когда то же самое произойдет, проверьте баланс округления.. У них есть 2/3 цента, поэтому теперь вы можете снять одну треть (так что теперь у них есть 1/3 цента в балансе округления ) и взимать только 3,33 .