OVH Cloud OVH Cloud

Conversions de type, perte d informations

33 réponses
Avatar
Dominique de LAPASSE
Bonjour,

Imaginons que j 'ai besoin de convertir la valeur 17.58 ( Euros ) en 1758
( Centimes )

Le calcul qui vient naturellement à l esprit est :


float fValeur1 = (float)17.58;
float fValeur2 = fValeur1 * 100;

long lValeur3;
lValeur3 = (long) fValeur2;

Or lValeur3 vaut 1757 !!!
Quel calcul ou conversions faut-il mettre en oeuvre pour parvenir a un
résultat correct ????

Merci.

10 réponses

1 2 3 4
Avatar
Vincent Lascaux
int ConversionEnCentimes (double euros)
{
return (euros * 100) + 0.5;
}



Ça « doit » marcher ? Vraiment ? Est-ce que c'est garanti par la
norme que les valeurs sont tronqués lorsqu'elles sont stockées ? Par
exemple, que 17.58 mis dans un double sera égal ou plus petit ? Je
ne crois pas (et je me demande si c'est ce que tu veux dire par
« dépend du mode d'arrondie »). Si la valeur peut être plus grande,
ce que je crois, alors le +0.5 fera passé à la prochaine unité, ce
qui est incorrect.


17.58 est stoqué avec une certaine incertitude, disons epsilon Donc la
valeur réelle de notre variable euros est dans [17.58-epsilon,
17.58+epsilon], et la valeur de euros*100 + 0.5 est dans [1758.5 -
100*epsilon, 1758.5 + 100*epsilon]
Si epsilon est plus petit que 0.005 (strictement), alors la partie entiere
de euros*100+0.5 est bien 1758

--
Vincent



Avatar
Dominique de LAPASSE
Ouhla,
Je pensais pas susciter autant de réponses, et techniques, et rigolotes...

Merci à tous en tout cas

Le Pompon revenant à Fabien, dont la solution est retenue.

Dominique
Avatar
Michel Michaud
Dans le message 4239b8e0$0$20750$,
int ConversionEnCentimes (double euros)
{
return (euros * 100) + 0.5;
}



Ça « doit » marcher ? Vraiment ? Est-ce que c'est garanti par la
norme que les valeurs sont tronqués lorsqu'elles sont stockées ?



J'ai mal regardé le contexte... :-(

[...]
Si epsilon est plus petit que 0.005 (strictement), alors la partie
entiere de euros*100+0.5 est bien 1758


Avec 1758, tout à fait. Je pensais à des cas comme 17.585 qu'on
voudrait arrondir à 1759, alors qu'on voudrait 17.584999 à 1758.
Dans ce cas, je continue à penser que ce n'est pas si simple...
Mais ce n'était pas le problème posé originalement.

Par ailleurs, pour avoir 1758 à partir de 17.58, on aurait tout
aussi bien pu additionner 0.9 ou 0.1; c'est ce 0.5 qui m'avait
perturbé :-).

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/




Avatar
Vincent Lascaux
Avec 1758, tout à fait. Je pensais à des cas comme 17.585 qu'on
voudrait arrondir à 1759, alors qu'on voudrait 17.584999 à 1758.
Dans ce cas, je continue à penser que ce n'est pas si simple...
Mais ce n'était pas le problème posé originalement.


Effectivement... mais j'ai du mal à imaginer des applications où l'on
voudrait ca...

Par ailleurs, pour avoir 1758 à partir de 17.58, on aurait tout
aussi bien pu additionner 0.9 ou 0.1; c'est ce 0.5 qui m'avait
perturbé :-).


Si on additionne 0.1 ou 0.9 au lieu de 0.5, la condition pour que ca marche
est que epsilon < 0.001 au lieu de 0.005.
C'est clair qu'avec des flottants et sur des nombres qui représentent des
sommes monnétaires (donc pas de 10^20 euros), on doit de toutes façon avoir
une précision suffisante pour que le epsilon soit plus petit que 0.001, mais
tant qu'à faire, autant assurer le coup en prenant la valeur qui tolère un
epsilon le plus grand possible

--
Vincent

Avatar
kanze
Michel Michaud wrote:
Dans le message
,

Fabien LE LEZ wrote:
int ConversionEnCentimes (double euros)
{
return (euros * 100) + 0.5;
}


Fait gaffe quand même. Le résultat dans certains cas dépend
de la mode d'arrondie (même si ça doit marcher dans ce cas
précis).


Ça « doit » marcher ? Vraiment ? Est-ce que c'est garanti par
la norme que les valeurs sont tronqués lorsqu'elles sont
stockées ? Par exemple, que 17.58 mis dans un double sera égal
ou plus petit ?


§4.9/1 : « An rvalue of a floating point type can be converted
to an rvalue of an integer type. The conversion truncates; that
is, the fractional part is discarded. »

Je ne crois pas (et je me demande si c'est ce que tu veux dire
par « dépend du mode d'arrondie »).


Je me le démande aussi:-). Ici, le mode d'arrondie est spécifiée
par la norme.

Si le résultat réel doit toujours être un nombre entier, la
formule ci-dessus marche bien. Si la valeur réele de euros*100
risque d'être près de 0,5, il y a risque d'erreur. Ce n'est pas
une panacée.

Si la valeur peut être plus grande, ce que je crois, alors le
+0.5 fera passé à la prochaine unité, ce qui est incorrect. Je
ne crois pas que la solution soit aussi simple, du moment où,
une fois stockée, on ne peut pas savoir quelle était vraiment
la valeur initialement obtenue...


J'avoue que je préfère moi aussi des fonctions bien définies,
avec un nom qui indique clairement ce qu'on fait, comme ciel,
floor ou trunc (toutes dans <math.h>). Mais la troncation lors
de l'affectation à un type entier est bien garantie, à condition
que la valeur tronquée est représentable dans le type entier,
évidemment. (Note que la règle de la conversion modulo vers un
type non signé ne s'applique pas ici. Les règles spéciales pour
conversion vers bool en revanche si.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
kanze
Falk Tannhäuser wrote:
Marc Boyer wrote:
Comme tu le dis, tant qu'on reste dans les euros, on garde
2 décimales (3 pour vendre de l'essence).


... et pour les factures de France Telecom. Puis, pour les
factures EDF, il faut 4 décimales.


Et les prix moyens lors d'une transaction boursière avec 5
décimales (mais c'est un prix « pour information »).

D'après ce que je crois (mais je ne suis pas spécialiste dans le
domaine), la comptabilité doit toujours tenir compte des 5
décimales (sauf pour la fisc, qui se contente de 0, c-à-d
d'ignorer les centîmes). Mais c'est difficile à trouver des cas
où ça se remarquera -- à peu près le seul endroit où des
fractions d'un centîme apparaît dans ma comptabilité à moi,
c'est dans le calcul du TVA, et alors, c'est un chiffre, exact à
cinq décimales, qui se trouve immédiatement arrondi à deux
décimales.

Note en revanche que l'utilisation d'un double pour le calcul de
la TVA peut donner des résultats erronés. Si le résultat exact
de la multiplication du prix par 0,196 donne quelque chose qui
se termine en 0,xx5, et que la représentation en double
correspond à 0,xx499999..., l'arrondie ne va pas se faire dans
le bon sens.

(Enfin, je me démande si ce n'est pas la TVA qui condition le
calcul avec 5 décimales. Parce qu'il faut 5 décimales pour
représenter un prix exact en centîmes multiplié par 0,196. Si on
calcule avec 5 décimales, qu'on fasse la somme des TVA sur les
lignes, ou le TVA sur la somme des lignes, on obtient le même
résultat.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Michel Michaud
Dans le message ,
Michel Michaud wrote:
Dans le message
,
Ça « doit » marcher ? Vraiment ? Est-ce que c'est garanti par
la norme que les valeurs sont tronqués lorsqu'elles sont
stockées ? Par exemple, que 17.58 mis dans un double sera égal



Tu as manqué la partie « mis dans un double »...

ou plus petit ?


§4.9/1 : « An rvalue of a floating point type can be converted
to an rvalue of an integer type. The conversion truncates; that
is, the fractional part is discarded. »


Je ne parlais pas de la troncation en entier, mais de l'erreur de
représentation dès que la valeur est mise dans le double (17.58
dans un double sera-t-il nécessairement plus petit que 17.58, par
exemple 17.5799999999999875 ou pourrait-il être plus grand,
par exemple pourrait-il devenir 17.5800000000000125.)

Mais voir mon autre message sur ce sujet.

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/


Avatar
James Kanze
Michel Michaud wrote:
Dans le message
,


Michel Michaud wrote:



Dans le message
,
Ça « doit » marcher ? Vraiment ? Est-ce que c'est garanti par
la norme que les valeurs sont tronqués lorsqu'elles sont
stockées ? Par exemple, que 17.58 mis dans un double sera égal




Tu as manqué la partie « mis dans un double »...


Oui.

ou plus petit ?




§4.9/1 : « An rvalue of a floating point type can be
converted to an rvalue of an integer type. The conversion
truncates; that is, the fractional part is discarded. »



Je ne parlais pas de la troncation en entier, mais de l'erreur
de représentation dès que la valeur est mise dans le double
(17.58 dans un double sera-t-il nécessairement plus petit que
17.58, par exemple 17.5799999999999875 ou pourrait-il être
plus grand, par exemple pourrait-il devenir
17.5800000000000125.)


Je ne comprends pas ce que tu veux dire. La valeur 17.58 ne
serait jamais mis dans un double. Si la source est un double, la
valeur n'est pas 17.58, au moins sur les machines actuelle. Et
si la est autre chose, il y a une conversion, et les règles de
conversion s'appliquent -- si le type cible est flottant, la
direction de l'arrondie dépend de l'implémentation, et si le
type cible est entier, il y a troncation vers zéro. Seulement,
que le type source soit entier ou qu'il soit flottant, sa valeur
n'est certainement pas 17.58.

Alors, quelle est ta question exactement ?

--
James Kanze home: www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34



Avatar
Michel Michaud
Dans le message 423c5679$0$28004$,
Je ne parlais pas de la troncation en entier, mais de l'erreur
de représentation dès que la valeur est mise dans le double
(17.58 dans un double sera-t-il nécessairement plus petit que
17.58, par exemple 17.5799999999999875 ou pourrait-il être
plus grand, par exemple pourrait-il devenir
17.5800000000000125.)


Je ne comprends pas ce que tu veux dire. La valeur 17.58 ne
serait jamais mis dans un double.


« Mis » était pris dans le sens général d'une affectation ou
lecture. Quelque part, quelqu'un pense que c'est 17.58...

double x= 17.58;
ou
cin >> x; // Et l'utilisateur tape 17.58

[...]
si le type cible est flottant, la
direction de l'arrondie dépend de l'implémentation


C'est exactement à quoi je pensais et ce dont je parlais. La
valeur finale, vraiment stockée, peut être plus grande. Et
ceci complique beaucoup toute tentative de « récupération »
de la valeur qu'on peut penser avoir stockée.

[...]
Alors, quelle est ta question exactement ?


Au départ, j'avais mal lu le contexte du message auquel j'ai
répondu :-(

Je voulais seulement dire qu'il ne suffit pas d'ajouter .5 après
avoir multiplié par une puissance de 10 pour récupérer une valeur
qu'on pense avoir stockée, avec une certaine précision (en entier
ou autre). Mais le message original parlait de récupérer seulement
deux décimales, ce qui est possible bien entendu (mais il n'est
pas nécessaire d'utiliser 0.5 alors... voir les autres messages).

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/


Avatar
Vincent Lascaux
Je voulais seulement dire qu'il ne suffit pas d'ajouter .5 après
avoir multiplié par une puissance de 10 pour récupérer une valeur
qu'on pense avoir stockée, avec une certaine précision (en entier
ou autre). Mais le message original parlait de récupérer seulement
deux décimales, ce qui est possible bien entendu (mais il n'est
pas nécessaire d'utiliser 0.5 alors... voir les autres messages).


Je ne suis pas sur que tu aies compris le principe de la solution :
Pour récuperer un nombre avec n décimales, on fait (int) ((x*10^n)+0.5) (où
10^n est 10 à la puissance n, pas 10 xor n), et pour que ca marche, il faut
et il suffit que x soit connu à 0.5*10^-n près.
Le message original parlait effectivement de récuperer seulement deux
décimales, mais la technique est applicable avec n'importe quel nombre de
décimales.
Le choix de 0.5 n'a rien d'annodin : c'est la valeur qui permet la plus
grande tolérance quand à l'écart entre x et sa valeur arrondie à la n-ième
décimale. Si on faisait (int)((x*10^n)+0.8) par exemple, ca ne marche que si
l'erreur sur x est de moins de 0.2*10^-n.

--
Vincent

1 2 3 4