2010-01-03 17 views
5
$onethird = 1.0/3; 
$fivethirds = 1.0/3+1.0/3+1.0/3+1.0/3+1.0/3; 
$half = 1.0/2; 
$threehalf = 1.0/2+1.0/2+1.0/2; 
var_dump($onethird + $fivethirds == $half + $threehalf); 

che emette false, ma come tutti sappiamo: 5/3+1/3=2=3/2+1/2Come risolvere questo problema in PHP?

Come risolvere questo problema?

risposta

5

Questo è uno dei problemi con la rappresentazione IEEE 754 di numeri in virgola mobile; le rappresentazioni non sono sufficientemente accurate per rappresentare tutti i numeri razionali.

Il modo per farlo è quello di confrontare la differenza nei confronti di un numero molto piccolo di vicinanza, piuttosto che l'uguaglianza:

abs(($onethird + $fivethirds) - ($half + $threehalf)) < 1e-8 
+1

Questa è la natura dei numeri in virgola mobile in generale, quindi cosa ha a che fare con lo standard IEEE 754? –

+0

PHP è scritto in C e quindi ottiene il supporto FP da esso, e la maggior parte dei runtime utilizza IEEE 754. Inoltre, IEEE 854 utilizza una radix variabile e quindi può supportare più precisione per alcuni numeri al costo di una certa velocità di elaborazione quando calcolando con loro. –

+0

È la natura dei numeri in virgola mobile in generale. Il valore di epsilon (qui 0,000000001) dipende dal numero di bit che rappresentano il float. – slebetman

4

Il problema deriva dai piccoli errori introdotti da Floating Point arithmetic. Questo non è specifico per PHP.

È possibile risolvere questo problema introducendo un piccolo fattore di "tolleranza", vale a dire controllando che il primo valore nella comparazione sia> = il secondo valore meno la tolleranza e < = il secondo valore più la tolleranza.

1
var_dump(abs($onethird + $fivethirds - $half + $threehalf) < 0.00001); 

vedi: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm anche: http://docs.sun.com/source/806-3568/ncg_goldberg.html

Questo è vero in tutti i linguaggi di programmazione.

Non sono ancora arrivato in nessuna lingua dove ciò sia fatto automaticamente quando i programmatori vogliono l'uguaglianza dei float. Qualcuno dovrebbe venire con un nuovo operatore, forse = ~ = per l'uguaglianza galleggiante che farebbe automaticamente il confronto delle diff con Epsilon:

if ($float1 =~= $float2) {... 

E 'fastidioso che ogni anno, da quando mi sono laureato, nel 2000, in questo periodo dell'anno alcuni novizi porranno questa domanda su alcuni newsgroup, forum o mailing list. Proprio il mese scorso ho risposto a questa domanda su comp.lang.tcl. E non sono solo i neofiti, due mesi fa ho dovuto spiegarlo al mio collega che ha sviluppato software da oltre 5 anni chiedendomi perché il suo codice Perl non funziona.

1

Come risolvere questo problema?

Cosa effettivo Problema che vuoi risolvere? Il tuo codice mostra solo che i numeri in virgola mobile hanno una precisione limitata - non è una novità.

Per la maggior parte delle applicazioni del mondo reale, l'input non è preciso al 100% in ogni caso e il risultato deve essere preciso con un paio di cifre decimali. I confronti di uguaglianza semplicemente non sono qualcosa di cui hai bisogno la maggior parte del tempo. Se lo fai, puoi fudge vedendo se il risultato si trova entro una piccola distanza predefinita da un determinato numero.

Se è necessaria una matematica decimale con precisione specifica, utilizzare le funzioni BC Math, ma rendersi conto che sono molto lente se utilizzate per calcoli complessi.

Problemi correlati