Правильные суммы с разделением сумм, противодействуя ошибкам округления
веб-приложения написанные на 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 .