2016-06-03 13 views
5

Używam biblioteki PHP bcmath do wykonywania operacji na numerach stacjonarnych. Spodziewałem się uzyskać ten sam zachowanie Decimal klasy Pythona, ale byłem dość zaskoczony, aby znaleźć następujące zachowanie Zamiast:PHP bcmath kontra Python dziesiętny

// PHP: 
$a = bcdiv('15.80', '483.49870000', 26); 
$b = bcmul($a, '483.49870000', 26); 
echo $b; // prints 15.79999999999999999999991853 

podczas korzystania Decimal S w Pythonie uzyskać:

# Python: 
from decimal import Decimal 
a = Decimal('15.80')/Decimal('483.49870000') 
b = a * Decimal('483.49870000') 
print(b) # prints 15.80000000000000000000000000 

Dlaczego jest to, że ? Ponieważ używam tego do wykonywania bardzo czułych operacji, chciałbym znaleźć sposób na uzyskanie w PHP tego samego wyniku, co w Pythonie (tj. (x/y) * y == x)

+0

Tak: '$ a = bcdiv ('15 .80' "483.49870000", 26); echo gettype ($ a), "drukuje" ciąg " –

+0

Który z nich jest poprawny, przy okazji? – Will

Odpowiedz

4

Po kilku eksperymentach odkryłem to. Jest to problem z zaokrąglaniem lub obcinaniem. Python domyślnie używa zaokrąglania ROUND_HALF_EVEN, a PHP po prostu obcina z określoną precyzją. Python ma domyślną dokładność równą 28, podczas gdy używasz 26 w PHP.

In [57]: import decimal 
In [58]: decimal.getcontext() 
Out[58]: Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1, flags=[], traps=[InvalidOperation, Overflow, DivisionByZero]) 

Jeśli chcesz, aby naśladować zachowanie Python PHP obcinania, po prostu trzeba zmienić właściwość :

In [1]: import decimal 
In [2]: decimal.getcontext().rounding = decimal.ROUND_DOWN 
In [3]: decimal.getcontext().prec = 28 
In [4]: a = decimal.Decimal('15.80')/decimal.Decimal('483.49870000') 
In [5]: b = a * decimal.Decimal('483.49870000') 
In [6]: print(b) 
15.79999999999999999999999999 

Making PHP zachowują się jak domyślnie Pythona jest nieco trudniejsze. Musimy utworzyć funkcję niestandardową dla podziału i mnożenia, że ​​rund "pół nawet" jak Python:

function bcdiv_round($first, $second, $scale = 0, $round=PHP_ROUND_HALF_EVEN) 
{ 
    return (string) round(bcdiv($first, $second, $scale+1), $scale, $round); 
} 

function bcmul_round($first, $second, $scale = 0, $round=PHP_ROUND_HALF_EVEN) 
{ 
    $rounded = round(bcmul($first, $second, $scale+1), $scale, $round); 

    return (string) bcmul('1.0', $rounded, $scale); 
} 

Oto demonstracji:

php > $a = bcdiv_round('15.80', '483.49870000', 28); 
php > $b = bcmul_round($a, '483.49870000', 28); 
php > var_dump($b); 
string(5) "15.80" 
+1

Wielkie dzięki za wspaniałą odpowiedź! –

+0

Bez problemu, z przyjemnością pomogę :) – Will