2009-06-15 14 views
9

Sto utilizzando il seguente codice per arrotondare a 2DP:Arrotondamento raddoppia - 0,5 - sprintf

sprintf(temp,"%.2f",coef[i]); //coef[i] returns a double 

Si arrotonda con successo 6,666-6,67, ma non funziona correttamente quando si aggira 5.555. Restituisce 5.55, mentre dovrebbe (almeno a mio parere) restituire 5.56.

Come posso ottenerlo per arrotondare quando la cifra successiva è 5? ad es. restituisce 5.56.

modifica: Ora mi rendo conto che questo sta accadendo perché quando inserisco 5.555 con cin diventa salvato come 5.554999997.

Ho intenzione di provare l'arrotondamento in due fasi: prima su 3dp e poi su 2dp. altre idee (più eleganti)?

risposta

11

Sembra che sia necessario utilizzare la funzione di arrotondamento matematico per arrotondare correttamente.

printf("%.2f %.2f\n", 5.555, round(5.555 * 100.)/100.); 

Questo ha pronunciato la seguente uscita sulla mia macchina:

5.55 5.56 
+0

Questo sembra un po 'più affidabile l'idea di aggiungere 0,0005, ma entrambi funzionerebbe immagino. Grazie per l'aiuto! – Meir

+0

Sembra un bug, davvero. 5.5551 viene arrotondato correttamente! – ypnos

+6

Non è bacato, @ypnos, non devi ancora superare i limiti del punto mobile. 5.5551 è rappresentato come 5.555100000000000370 ... che andrà a finire, 5.555 è reputato come 5.5549999999 ... che andrà a fondo. Puoi vedere che si tratta di un problema di rappresentazione provando il tuo codice con 5.5500000000000000001 - entrambi gli output sono 5.55 errati in quel caso perché il numero NON è 5.55.000 ... 1 quando è memorizzato. E '5.549999 ... – paxdiablo

11

Il numero 5.555 non può essere rappresentato come un numero esatto in IEEE754. Stampare le costanti 5.555 con "%.50f" risultati in:

5.55499999999999971578290569595992565155029300000000 

quindi sarà arrotondate. Provare a utilizzare questo, invece:

printf ("%.2f\n",x+0.0005); 

anche se bisogna stare attenti di numeri che possono essere rappresentato esattamente, in quanto essi saranno arrotondati a torto con questa espressione.

È necessario comprendere i limiti delle rappresentazioni in virgola mobile. Se è importante ottenere la precisione, è possibile utilizzare (o codificare) un GAV o un'altra classe decimale che non presenta la mancanza della rappresentazione IEEE754.

+0

Ciò non influisce sulla risposta, ma ero solo curioso: che lingua hai usato per stampare 5.55499999999999971578290569595992565155029300000000? In realtà dovrebbe essere 5.55499999999999971578290569595992565155029296875000 –

+0

@Rick, molto probabilmente gcc sotto CygWin ma è difficile da ricordare da un anno :-) Ottengo lo stesso risultato di Ubuntu10. Ma penso che i doppi garantiscano comunque solo 15 cifre decimali di accuratezza, quindi potrebbe utilizzare un formato interno più ampio (ho un vago ricordo che calcoli su almeno un sistema utilizzato internamente da 80 bit, ma che sarebbe stato tanto tempo fa - per arrivare a 48 cifre decimali come con il tuo numero, avresti bisogno di circa 160 bit di precisione). – paxdiablo

+0

OK, grazie. Sapevo che non poteva essere Visual C++ (non è possibile stampare tante cifre in modo accurato) e sembrava avere troppe poche cifre per gcc/glibc su Linux (che consente di stamparle tutte). In ogni caso, è possibile ottenere quel numero di cifre con un doppio standard (53 bit) - è la rappresentazione decimale esatta di ciò che detiene il doppio (anche se può essere solo un'approssimazione di 15 cifre di ciò che è stato assegnato ad esso). –

-2

Si potrebbe anche fare questo (salva moltiplicare/divisione):

printf("%.2f\n", coef[i] + 0.00049999); 
+0

Ciò consentirà di arrotondare 5.549999 a 5.56 ;-) – ypnos

+0

@ypnos, è davvero necessario controllare le cose prima di postare: NON le dà affatto 5.56. – paxdiablo

+1

Sconsiderato malinteso. Dovresti ancora vedere il mio punto. 5.544999 + 0.0005 viene arrotondato a 5.55. Dovrebbe essere chiaramente arrotondato a 5,54. Non prende la scienza missilistica per rendersi conto che l'aggiunta di un pregiudizio non risolverà misteriosamente un problema di arrotondamento senza introdurne di nuovi. – ypnos

1

Questa questione è aggiunto C++, quindi mi procedere secondo questa ipotesi. Si noti che gli stream C++ andranno in tondo, a differenza della famiglia C printf. Tutto quello che devi fare è fornire la precisione che desideri e la libreria degli stream si occuperà di te. Lo sto lanciando solo nel caso in cui non avessi già una ragione per non usare i flussi.

2

Che ne dite di questo per un'altra possibile soluzione:

printf("%.2f", _nextafter(n, n*2)); 

L'idea è di aumentare il numero per eccesso (il n * 2 ottiene il cartello a destra) per la più piccola quantità rappresentabile possibile dalla matematica in virgola mobile.

Esempio:

double n=5.555; 
printf("%.2f\n", n); 
printf("%.2f\n", _nextafter(n, n*2)); 
printf("%.20f\n", n); 
printf("%.20f\n", _nextafter(n, n*2)); 

Con rendimenti MSVC:

5.55 
5.56 
5.55499999999999970000 
5.55500000000000060000