2009-10-22 6 views
13

Ancora un altro problema di calcolo matematico.calcolo float php 2 punto decimale

$a = 34.56 

$b = 34.55 

$a fare qualche calcolo per ottenere questa cifra

$b sta facendo arrotondamento più vicino 0,05 per ottenere questa cifra

ciò accada è

$c = $b - $a 

suppone che dovrebbe essere -0.01, ma echo il $c è spettacolo -0.00988888888888

cerco di usare n umber_format($c, 2), ma l'uscita è 0,00,

come posso fare in modo $a e $b è esattamente 2 decimali, nessun numero nascosta sul retro.

nel mio php conoscenza number_format solo in grado di formattare il display, ma il valore non proprio 2 decimali,

Spero di poter ricevere aiuto da qui. Questo mi ha davvero frustrato.

+0

Esamina "precisione" in php.ini per ulteriori modifiche a livello di PHP. Tuttavia, tieni presente che hai a che fare con * cifre significative * lì. –

risposta

34

Prova sprintf("%.2f", $c);

numeri in virgola mobile sono rappresentati in IEEE notazione basata sulle potenze di 2, in modo da terminando numeri decimali non può essere un numero binario di terminazione, è per questo che si ottiene le cifre finali.

Come suggerito da Codificatore di lunghezza variabile, se si conosce la precisione desiderata e non cambia (ad esempio quando si ha a che fare con denaro) potrebbe essere meglio usare solo numeri a virgola fissa, cioè esprimere i numeri come centesimi piuttosto che dollari

$a = 3456; 

$b = 3455; 

$c = $b - $a; 

sprintf ("%.2f", $c/100.0); 

in questo modo, non sarà necessario alcun errori di arrotondamento se si fa un sacco di calcoli prima della stampa.

+0

grazie per la tua risposta, l'unico modo è quello di formattare l'output, non è possibile impostarlo su 2 decimali, eccetto l'uso di String. – Shiro

+0

Se si desidera impostarlo su 2 decimali, è necessario utilizzare numeri a virgola fissa, non a virgola mobile. Vedi la risposta del codificatore a lunghezza variabile. –

+0

Non capisco davvero cosa significhi per numeri "a virgola fissa", puoi gentilmente fornire qualche esempio? Molte grazie! – Shiro

12

Uso round():

$c = round($b - $a, 2); 

Nota: si può anche scegliere la modalità di arrotondamento a seconda dei casi.

Edit: Ok non ottengo quello che sta facendo sprintf() che number_format() non è:

$c = number_format($b - $a, 2); 

vs

$c = sprintf("%.2f", $b - $a); 

?

+0

Probabilmente niente, ma sprintf() è molto più divertente da usare per ragazzi come me che sono abituati a C. Inoltre salva uno scritto '$ foo = numero_formato ($ b - $ a, 2). "mele!" 'e invece scrivendo' $ foo = sprintf ("%. 2f mele!", $ b - $ a) '- molto più elegante, secondo me. –

+0

Rocce rotonde, forse perché è tutto ciò che so per ora, ancora imparando. – Newb

2

Ti sei imbattuto in una trappola di numeri in virgola mobile; che non possono sempre rappresentare le frazioni decimali esatte. Se si desidera valori decimali esatti, è meglio utilizzare gli integer e quindi dividere per la precisione desiderata alla fine.

Ad esempio, se si stanno eseguendo calcoli in virgola mobile che rappresentano Dollari (o la valuta preferita), si consiglia di eseguire effettivamente i calcoli in centesimi interi.

+0

puoi per cortesia dare un esempio di come funzionano i centesimi interi? Non hai davvero ciò che intendi. Grazie mille per il tuo commento. – Shiro

+0

"Numero intero" significa che, ad esempio, l'importo di $ 24.95 deve essere memorizzato non come un 24,95 a virgola mobile, ma piuttosto come l'intero 2495. L'importo di $ 10,00 verrà memorizzato come numero intero 1000. E così via. –

2

È possibile eludere in modo molto preciso tutti questi problemi semplicemente utilizzando la libreria bcmath.

Ricordarsi di leggere la documentazione e fare attenzione se si passano argomenti come stringhe o come tipi di dati numerici.

4

Calcolo Native:

$a = 34.56; 
$b = 34.55; 
$c = $b - $a; // -0.010000000000005  

funziona come previsto (utilizzare sempre le funzioni BC per i calcoli numero reale, il problema è per tutte le piattaforme basate C!):

$a = '34.56'; 
$b = '34.55'; 
$c = bcsub($b, $a, 4); // -0.0100  
+0

Per un'ulteriore iterazione su questo. Utilizza le stringhe per rappresentare i dati anziché i tipi di dati mobili/doppi. Quindi utilizzare le funzioni di BCMath per calcolare le variabili. Per utilizzare sprintf ('% 1 $ s', bcadd ($ var1, $ var2)); Lo stesso vale per l'archiviazione dei dati dei database. http://php.net/manual/en/book.bc.php – fyrye

3

I anche questo problema si è verificato di recente quando si eseguono calcoli con float. Ad esempio, avevo 2 float che, sottratti e formattati, il valore era -0.00.

$floatOne = 267.58; 
$floatTwo = 267.58; 
$result = number_format($floatOne - floatTwo, 2); 
print $result; //printed a string -0.00 

Quello che ho fatto è stato:

$result = abs($floatOne - $floatTwo);// Made the number positive 
print money_format('%i', $result); // printed the desired precision 0.00 

Nella mia soluzione so che floatOne non sarà mai inferiore a floatTwo. La funzione money_format è definita solo se il sistema ha capacità strfmon, Windows no.

1

Se ancora qualcuno raggiunge questa pagina con problemi simili in cui la sottrazione di numeri mobili causa errori o valori strani. Voglio spiegare questo problema con un po 'più di dettagli. Il colpevole sono i numeri in virgola mobile. E diversi sistemi operativi e diverse versioni dei linguaggi di programmazione possono comportarsi diversamente.

Per illustrare il problema, spiegherò perché con un semplice esempio di seguito.

Non è direttamente correlato a PHP e non è un bug. Tuttavia, ogni programmatore dovrebbe essere a conoscenza di questo problema.

Questo problema ha richiesto anche molte vite due decenni fa.

il 25 febbraio 1991 questo problema in galleggiamento il calcolo del numero in una batteria di missili MIM-104 Patriot impedito intercettare un missile Scud in entrata a Dhahran, Arabia Saudita, contribuendo alla morte di 28 soldati dal 14 Quartermaster Detachment del Stati Uniti Army.

Ma perché succede?

Il motivo è che i valori in virgola mobile rappresentano una precisione limitata. Pertanto, un valore potrebbe non avere la stessa rappresentazione di stringa dopo qualsiasi elaborazione. Inoltre, include la scrittura di un valore in virgola mobile nello script e direttamente stampandolo senza operazioni matematiche.

Basta un semplice esempio:

$a = '36'; 
$b = '-35.99'; 
echo ($a + $b); 

che ci si aspetta per stampare 0.01, giusto? Ma stamperà una risposta molto strana come 0.009999999999998

Come altri numeri, i numeri in virgola mobile doppi o float vengono archiviati in memoria come una stringa di 0 e di 1.In che modo il punto variabile differisce dal numero intero è nel modo in cui interpretiamo gli 0 e gli 1 quando vogliamo guardarli. Esistono molti standard su come vengono memorizzati.

numeri a virgola mobile sono in genere confezionati in un dato computer come bit di segno, il campo esponente, e il significante o mantissa, da sinistra a destra ....

numeri decimali non sono ben rappresentati in binario a causa della mancanza di spazio sufficiente. Quindi, non puoi esprimere 1/3 esattamente come è 0.3333333 ..., giusto? Perché non possiamo rappresentare 0,01 come un numero di float binario per lo stesso motivo. 1/100 è 0,00000010100011110101110000 ..... con una ripetizione 10100011110101110000.

Se 0.01 è mantenuto in forma semplificata e sistema troncato di 01000111101011100001010 in binario, quando viene tradotto in decimale, sarebbe letto come ,0099,999 mila. ... a seconda del sistema (i computer a 64 bit offrono una precisione molto migliore rispetto a 32 bit). Il sistema operativo decide in questo caso se stamparlo come vede o come farlo in modo più leggibile. Quindi, dipende dalla macchina come vogliono rappresentarlo. Ma può essere protetto a livello di lingua con metodi diversi.

Se si formatta il risultato, echo number_format (0,009999999999998, 2); stamperà 0,01.

È perché in questo caso si indica come dovrebbe essere letto e la precisione richiesta.