2016-06-03 20 views
5

Sto utilizzando la libreria PHP bcmath per eseguire operazioni su numeri in virgola fissa. Mi aspettavo di ottenere lo stesso comportamento delle classi di Python Decimal ma sono rimasto molto sorpreso di trovare il comportamento seguente invece:PHP bcmath rispetto a Python Decimal

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

durante l'utilizzo Decimal s in Python ottengo:

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

Perché è che ? Come sto usando questo per eseguire operazioni molto sensibili, vorrei trovare un modo per ottenere in PHP lo stesso risultato come in Python (cioè (x/y) * y == x)

+0

Sì: '$ a = bcdiv ('15 .80' , '483.49870000', 26); echo gettype ($ a); 'stampa" stringa " –

+0

Quale è corretta, btw? – Will

risposta

4

Dopo qualche sperimentazione, l'ho capito. Questo è un problema con arrotondamento e troncamento. Python, per impostazione predefinita, utilizza l'arrotondamento ROUND_HALF_EVEN, mentre PHP semplicemente tronca alla precisione specificata. Python ha anche una precisione predefinita di 28, mentre stai usando 26 in 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]) 

Se si vuole fare in Python imitare il comportamento di PHP di troncamento, abbiamo solo bisogno di modificare la proprietà :

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 

Fare PHP si comportano come il default di Python è un po 'più complicato. Abbiamo bisogno di creare una funzione personalizzata per la divisione e moltiplicazione, che giri "mezzo anche" come 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); 
} 

Ecco una dimostrazione:

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

Grazie mille per la tua grande risposta! –

+0

Nessun problema, felice di aiutare :) – Will