2013-08-09 10 views
6

W poniższym kodzie, dlaczego metoda mnożenia nie generuje błędów zaokrąglania, podczas gdy skumulowanym podejściem dodawania jest?Dlaczego otrzymuję inny wynik z dwóch rzekomo analogicznych operacji arytmetycznych?

function get_value() { return 26.82; } 

function a($quantity) { 
    $value_excluding_vat = get_value(); 
    $value_including_vat = round($value_excluding_vat * (1 + (20/100)),2); 
    $total_nett = 0; 
    $total_gross = 0; 
    for($i=0; $i<$quantity; $i++) { 
     $total_nett += $value_excluding_vat; 
     $total_gross += $value_including_vat; 
    } 
    return array(
     $total_nett, 
     $total_gross 
    ); 
} 

function b($quantity) { 
    $value_excluding_vat = get_value(); 
    $value_including_vat = round($value_excluding_vat * (1 + (20/100)),2); 
    return array(
     $quantity * $value_excluding_vat, 
     $quantity * $value_including_vat 
    ); 
} 

$totals = a(1000); 
print_r($totals); 
echo $totals[1] - $totals[0]; 
echo "\n\n"; 
$totals = b(1000); 
print_r($totals); 
echo $totals[1] - $totals[0]; 

Oto moje wyjście:

Array 
(
    [0] => 26820 
    [1] => 32180 
) 
5360.0000000005 

Array 
(
    [0] => 26820 
    [1] => 32180 
) 
5360 
+0

Możliwa duplikat http: // stackoverflow. com/questions/11366522/php-floating-point-errors-with-basic-mathematics – BLaZuRE

+9

Przeczytałem twoje pytanie jako: _ "Miałem kod A (nie pokazano), który zwrócił wynik X podczas mnożenia i zaokrąglania liczb zmiennoprzecinkowych. Zmieniłem to na kod B (nie pokazano), który daje wynik Y. Dlaczego tak się dzieje? "_ Odpowiedź brzmi: zależy to od kodu i prawdopodobnie ma związek z błędami zaokrąglania. Pokaż swój aktualny, odpowiedni kod i – CodeCaster

+0

Rozważ użycie argumentu $ mode na [round()] (http://php.net/manual/en/function.round.php): domyślnie jest PHP_ROUND_HALF_UP –

Odpowiedz

6

Po pierwsze, uważają, że istnieje wiele liczb, które są racjonalne w bazie 10 , ale nie w binarnej reprezentacji zmiennoprzecinkowej. Na przykład wartość zmiennoprzecinkowa wynosząca 26,82 to w rzeczywistości 26.8200000000000002842170943040400743484497

Naturalnie, jeśli nadal dodajesz do siebie kilka błędów, ale do 15 cyfr znaczących powinno być w porządku - dodaj to 1000 razy, a suma jest faktycznie 26819.9999999997671693563461303710937500000000

Interesujące pytanie brzmi, gdy mamy wiele 26.82 na 1000.0 otrzymujemy 26820.0000000000000000000000000000000000000000 - jak to się robi?

Odpowiedź jest po prostu, że 26.820,0 robi mieć dokładną reprezentację binarną, a operacja mnożenia jest wystarczająco silny, aby dostrzec, że - nawet pomnożenie przez 1001.0 i odjęcie 26.82 nadal Ci dokładną odpowiedź.

Oto kilka interesujących linków

3

Problem może być w machine representation of float values (patrz ostrzegawczy blok)

np 21470.73 może być 21470.729999..9994561 lub 21470.73000 ... 00, zależy od tego, w jaki sposób zostały obliczone.

Spróbuj zaokrąglić wartości tymczasowe jak gross_total_so_far i nett_total_so_far przed obliczyć $total

1

nie mogę odgadnąć problemu liczbę zmiennoprzecinkową reprezentacji i odejmowanie, ale

kiedy będzie odjąć te wartości bez rundzie Otrzymasz wynik ** 3578.455

A kiedy zaokrąglisz go do dwóch miejsc po przecinku, to zaokrąglasz z 3578.46.

Więc php ma rozwiązanie tego problemu.

PHP_ROUND_HALF_UP Round val up to precision decimal places away from zero, when it is half way there. Making 1.5 into 2 and -1.5 into -2. 

PHP_ROUND_HALF_DOWN  Round val down to precision decimal places towards zero, when it is half way there. Making 1.5 into 1 and -1.5 into -1. 

PHP_ROUND_HALF_EVEN  Round val to precision decimal places towards the next even value. 

PHP_ROUND_HALF_ODD Round val to precision decimal places towards the next odd value. 

Te stałe są dostarczane z okrągłym funkcji jak

echo round(100.675, 2, PHP_ROUND_HALF_UP); // 100.68 
echo round(100.675, 2, PHP_ROUND_HALF_DOWN); // 100.67 

Więc PHP_ROUND_HALF_DOWN będzie przydatna w przypadku

Powiązane problemy