2012-07-23 12 views
8

Web-Anwendung in PHP mit einer MySQL-Datenbank codiert.Korrekte Summen mit Trennsummen, um Rundungsfehlern entgegenzuwirken

Ich habe ein System, das unterschiedliche Kosten für eine Anzahl von Leuten berechnet, wenn man Kosten aufteilt. Zum Beispiel Person A kauft etwas für 10 und Personen B, C und D sollten die Kosten aufteilen.

Das System sollte eine positive Datensatz für Person A von 10 und negativen Datensätze von 10/3 für B, C und D,

jedoch dafür registrieren, wenn dies durchgeführt wird; B, C und D haben alle -3,33 nach dem Runden. Was natürlich addiert nicht zu den insgesamt 10. Was ist der beste Weg, um über dieses Problem zu gehen? Eine optimale Lösung wäre zufällig, welche Person die etwas größeren Kosten bekommen.

Eine mögliche Lösung ist, wenn ich lasse nur die Schulden 10 - (A + B) seine letzten Menschen, aber dann gibt es ein Problem, wenn vier Personen Kosten in Höhe von zum Beispiel 13,34 aufgeteilt. Die verschiedenen Teile wären dann 3,34, 3,34, 3,34 und 3,32, während die optimale Aufteilung 3,34, 3,34, 3,33, 3,33 wäre.

Einige könnten argumentieren, dass mit ausreichende Dezimalzahlen dies nur ein Problem ist, wenn große Mengen von Zeilen haben. Aber in einem wirtschaftlichen System denke ich, dass es wichtig ist, ein ausfallsicheres System von Anfang an zu haben. Es muss skalierbar sein, und kann nicht einmal den geringsten Fehler haben. Unfairness ist in Ordnung, nur keine Fehler.

ähnliches Problem: sum divided values problem (dealing with rounding error)

+0

"Ungerechtigkeit ist in Ordnung, nur keine Fehler." - Ich liebe es – user1020317

+0

+1 Präsentation – Smandoli

+0

Ich denke, ich würde die beiden nächsten teilenden Werte finden, dann zwischen den beiden umschalten, wie sie zugewiesen sind. Dann überlegen Sie, was Sie mit dem (potentiellen) letzten ungeraden Wert machen wollen. – Smandoli

Antwort

1

Dies scheint zu funktionieren - http://jsfiddle.net/nQakD/.

Verwendet jQuery als Beispiel, aber wenn Sie PHP kennen, sollten Sie in der Lage sein, es einfach in PHP zu konvertieren. Wenn du auch PHP-Code brauchst, sag es mir, ich werde es für dich schreiben.

Ich werde den Code auch hier einfügen -

$(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:

Und hier ist PHP-Code arbeiten -

$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:

Dieser sh ould beheben das js Problem - http://jsfiddle.net/nQakD/ aktualisierten Code oben auch.

EDIT 3:

Edited PHP-Code und JS-Code, so funktioniert es für alle Beispiele - http://jsfiddle.net/nQakD/.

+0

Vielen Dank! Ein paar Fragen jedoch: ** 1. ** Wenn Casting $ Zahlung als int, ergibt es 3.33 oder 3.34, dh. abgerundet oder nicht? ** 2. ** Wenn Sie verschiedene Werte in Ihrem wunderbaren js-Skript ausprobieren, kann ich Werte erhalten, die nicht auf zwei Dezimalstellen gerundet sind. Sollte ich irgendwo in der if-Anweisung einen ähnlichen Int-Cast machen? –

+0

1) Es wird 3.33 ergeben. 2) Kannst du mir ein paar Beispiele geben? – y2ok

+0

Überprüfen Sie die neue Antwort. – y2ok

0

Vielleicht eine neue "Rundungs ​​Balance" macht für jeden Benutzer auf. Wenn es 10 auf drei Arten geteilt werden würde, würde jeder Benutzer 3,34 bezahlen. Jeder Benutzer hätte auch 2/3 eines Cent zu seinem Rundungssaldo. (Dies müsste als Text gespeichert werden, nehme ich an).

Das nächste Mal das gleiche passiert, überprüfen Sie die Rundungssaldo .. Sie haben 2/3 Cent, so dass Sie jetzt ein Drittel ausnehmen können (so haben sie jetzt 1/3 Cent in der Rundungssaldo) und nur 3.33 berechnen.