2009-06-15 8 views
9

J'utilise le code suivant pour arrondir à 2DP:Arrondi double - 0,5 - sprintf

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

Il arrondit avec succès 6,666 à 6,67, mais il ne fonctionne pas correctement lorsque l'arrondissement 5,555. Il renvoie 5,55, alors qu'il devrait (au moins à mon avis) retourner 5,56.

Comment puis-je l'arrondir lorsque le chiffre suivant est 5? c'est-à-dire retour 5.56.

edit: Je réalise maintenant que cela se produit parce que quand j'entre 5.555 avec cin il obtient enregistré comme 5.554999997.

Je vais essayer d'arrondir en deux étapes, d'abord à 3dp puis à 2dp. (des idées plus élégantes)?

Répondre

11

Il semble que vous ayez à utiliser la fonction round math pour un arrondi correct.

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

Cela donne la sortie suivante sur ma machine:

5.55 5.56 
+0

Cela semble un peu plus fiable que l'idée d'ajouter 0,0005, mais les deux fonctionnerait je suppose. Merci de votre aide! – Meir

+0

Cela semble être un bug, vraiment. 5.5551 est arrondi correctement! – ypnos

+6

Ce n'est pas buggé, @ypnos, vous n'avez tout simplement pas encore les limites du flottant. 5.5551 est représenté par 5.555100000000000370 ... qui sera arrondi, 5.555 est représenté par 5.5549999999 ... qui sera arrondi à la baisse. Vous pouvez voir que c'est un problème de représentation en essayant votre code avec 5.5500000000000000001 - les deux sorties sont les 5.55 incorrectes dans ce cas parce que le nombre N'EST PAS 5.55000 ... 1 quand il est stocké. C'est 5.549999 ... – paxdiablo

11

Le nombre 5.555 ne peut pas être représenté par un nombre exact à IEEE754. Impression des 5.555 constants avec "%.50f" résultats dans:

5.55499999999999971578290569595992565155029300000000 

il sera arrondi. Essayez d'utiliser ceci:

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

bien que vous devez faire attention de chiffres qui peuvent être représenté exactement, car ils seront rassemblés à tort par cette expression.

Vous devez comprendre les limites des représentations à virgule flottante. S'il est important que vous obteniez une précision, vous pouvez utiliser (ou coder) un BCD ou une autre classe décimale qui ne présente pas le défaut de représentation IEEE754.

+0

Cela n'affecte pas votre réponse, mais j'étais simplement curieux: quelle langue avez-vous utilisé pour imprimer 5.55499999999999971578290569595992565155029300000000? Il devrait être 5.55499999999999971578290569595992565155029296875000 –

+0

@Rick, probablement gcc sous CygWin mais il est difficile de se souvenir d'il y a un an :-) J'ai le même résultat que sous Ubuntu10. Mais je pense que les doubles ne garantissent que 15 chiffres décimaux de précision, de sorte qu'il peut utiliser un format interne plus large (je me souviens vaguement que les calculs sur au moins un système utilisé 80bits en interne, mais il y a longtemps - 48 chiffres décimaux comme avec votre numéro, vous auriez besoin d'environ 160 bits de précision). – paxdiablo

+0

OK, merci. Je savais qu'il ne pouvait pas être Visual C++ (il ne peut pas imprimer autant de chiffres avec précision) et il semblait avoir trop peu de chiffres pour gcc/glibc sous Linux (ce qui vous permet de les imprimer tous). Dans tous les cas, vous pouvez obtenir autant de chiffres avec un double standard (53 bits) - c'est la représentation décimale exacte de ce que le double contient (même s'il ne s'agit que d'une approximation de 15 chiffres de ce qui lui a été assigné). –

-2

Vous pouvez aussi le faire (Savès multiplier/diviser):

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

Cela tournera 5.549999 à 5.56 ;-) – ypnos

+0

@ypnos, vous avez vraiment besoin de vérifier les choses avant de poster - il ne vous donne pas 5.56 du tout. – paxdiablo

+1

Désolé une idée fausse. Vous devriez toujours voir mon point. 5.544999 + 0.0005 est arrondi à 5.55. Il devrait être clairement arrondi à 5,54. Ne prend pas la science de fusée pour réaliser que l'ajout d'un biais ne sera pas résoudre mystérieusement un problème d'arrondi sans en introduire de nouveaux. – ypnos

1

Cette question est étiquetée C++, donc je vais continuer dans cette hypothèse. Notez que les flux C++ arrondiront, contrairement à la famille C printf. Tout ce que vous avez à faire est de fournir la précision que vous voulez et la bibliothèque de flux tournera pour vous. Je suis juste en train de jeter ça au cas où vous n'avez pas de raison de ne pas utiliser de flux.

2

Que diriez-vous cela pour une autre solution possible:

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

L'idée est d'augmenter le nombre loin de zéro (n * 2 obtient le signe à droite) par le plus petit représentable quantité possible en flottant mathématiques points.

Par exemple:

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)); 

Avec des rendements MSVC:

5.55 
5.56 
5.55499999999999970000 
5.55500000000000060000