OVH Cloud OVH Cloud

Question d'arrondissement

12 réponses
Avatar
Philippe Massicotte
Bonjour à tous.

Tout d'abord excusé moi si je ne suis pas au bon endroit pour ma question.

Je suis présentement un cours de calcul numérique et je dois programmer une
méthode bien précise (Runge-Kutta pour ceux que cela pourrait intéresser).

Si je fais

double j = 1 * 0.1;

mon compilateur (VC++ .NET 2003) me donne comme valeur ceci :
0.10000000000000001

Le 1 de la fin fait en sorte que je cumule une erreur tout au long du
déroulement qui s'amplifie bien entendu.

Quelqu'un peut m'expliquer comment résoudre cette impasse ?

Merci à l'avance,

Philippe

10 réponses

1 2
Avatar
Quentin Pouplard
Philippe Massicotte wrote:
Bonjour à tous.

Tout d'abord excusé moi si je ne suis pas au bon endroit pour ma
question.

Je suis présentement un cours de calcul numérique et je dois
programmer une méthode bien précise (Runge-Kutta pour ceux que cela
pourrait intéresser).

Si je fais

double j = 1 * 0.1;

mon compilateur (VC++ .NET 2003) me donne comme valeur ceci :
0.10000000000000001

Le 1 de la fin fait en sorte que je cumule une erreur tout au long du
déroulement qui s'amplifie bien entendu.

Quelqu'un peut m'expliquer comment résoudre cette impasse ?



En gros, le problème vient du fait qu'un processeur calcule en binaire,
un nombre n'est donc N*10^qqch mais N*2^qqch.

2 solutions:
- si tu connais la précision nécessaire ("le nombre de chiffres après la
virgule") et qu'elle est constante, tu peux utiliser des virgules fixes,
pratiquement, tu manipules des int, en fixant arbitrairement leur
valeur. (note: attention au multiplication).
- si c'est variable, il te faut créer une nouvelle structure de donnée
permettant de représenter les nombres décimaux. Certains langage
fournissent cela en natif, il faut donc voir ce que tu utilises comme
langage.

ps: cette question n'est pas vraiment de la programmation windows.
re-ps: ce n'est pas ton compilo, mais ton CPU la source d'erreur.


--
Quentin Pouplard (Tene/MyOE)
http://www.myoe.org | http://graff.alrj.org
Avatar
PendLoup
Bonjour,

"Quentin Pouplard" wrote

re-ps: ce n'est pas ton compilo, mais ton CPU la source d'erreur.




Par curiosité, j'ai fais le test sous Visual Studio Net et Visual 6, sur la
même machine.

Dans le premier cas : double j = 0.1; donne 0.10000000000000001
alors qu'avec Visual 6 j'obtiens 0.1

Les 2 compilateurs semblent donc différents.

Fred D.


Enlevez NoSpam de mon adresse pour me répondre
Avatar
Manuel Leclerc
Philippe Massicotte a écrit :

Si je fais

double j = 1 * 0.1;

mon compilateur (VC++ .NET 2003) me donne comme
valeur ceci : 0.10000000000000001

Le 1 de la fin fait en sorte que je cumule une erreur
tout au long du déroulement qui s'amplifie bien entendu.

Quelqu'un peut m'expliquer comment résoudre cette impasse ?



La méthode bourrin de chez bourrin mais qui fonctionne assez
bien dans un environnement d'informatique de gestion, peut
être peux-tu t'en inspirer, même si ton truc a l'air
plutôt scientifique/industriel.

Passe par une représentation sous forme de chaînes de caractères
en demandant une précision de quelques chiffres inférieures à la
précision de ta représentation flottantes.
sprintf/sscanf après chaque calcul. Avec ça, tu peux cumuler autant
d'opération bancaires que tu veux sans te retrouver avec des
centimes en "génération spontanée". Mais, je t'avais prévenu,
c'est bourrin de chez bourrin :-). Et bonjour les perfs.

Travailler en entier peut aussi convenir d'ailleurs. Ca dépends
du nombres de digit significatifs que tu veux, et des opérations
a effectuer.

Il y aussi peut être une fonction d'arrondi, tout simplement !
mais je connais très mal le couple C/flottants comme tu viens
peut être de t'en rendre compte :-)
Avatar
Philippe Massicotte
"Quentin Pouplard" a écrit dans le message de
news:
En gros, le problème vient du fait qu'un processeur calcule en binaire,
un nombre n'est donc N*10^qqch mais N*2^qqch.



Ah merci de l'explication !

2 solutions:
- si tu connais la précision nécessaire ("le nombre de chiffres après la
virgule") et qu'elle est constante, tu peux utiliser des virgules fixes,
pratiquement, tu manipules des int, en fixant arbitrairement leur
valeur. (note: attention au multiplication).



Je ne saisi pas tout à fait votre réponse ici. En fait, je voudrait avoir
une précision de 10 nombres après la virgule.

Si ce n'est pas trop compliqué, pouvez-vous me donner un exemple simple ?.

Merci encore :)

Phil
Avatar
Quentin Pouplard
PendLoup wrote:
Bonjour,

"Quentin Pouplard" wrote

> re-ps: ce n'est pas ton compilo, mais ton CPU la source d'erreur.


Par curiosité, j'ai fais le test sous Visual Studio Net et Visual 6,
sur la même machine.

Dans le premier cas : double j = 0.1; donne 0.10000000000000001
alors qu'avec Visual 6 j'obtiens 0.1

Les 2 compilateurs semblent donc différents.



Quel option de compilation as-tu utilisé sur tes deux compilos?



--
Quentin Pouplard (Tene/MyOE)
http://www.myoe.org | http://graff.alrj.org
Avatar
Quentin Pouplard
Philippe Massicotte wrote:
"Quentin Pouplard" a écrit dans le message de
news:
> En gros, le problème vient du fait qu'un processeur calcule en
> binaire, un nombre n'est donc N*10^qqch mais N*2^qqch.

Ah merci de l'explication !

> 2 solutions:
> - si tu connais la précision nécessaire ("le nombre de chiffres
> après la virgule") et qu'elle est constante, tu peux utiliser des
> virgules fixes, pratiquement, tu manipules des int, en fixant
> arbitrairement leur valeur. (note: attention au multiplication).

Je ne saisi pas tout à fait votre réponse ici.



Le concept est d'utiliser des entiers, par exemple 100, 1203, etc... et
de déterminer arbitraitement que 100 est en fait 1.00 et 1203 est 12.03.
Les calculs entiers ne souffrant pas de l'imprécision des flotants, il
n'y a plus d'erreur d'arrondi... par contre tu te retrouves avec les
limitations des entiers: si tu prends des entiers 32, ça ne va au mieux
que jusqu'à un peu plus de 4 milliards, ce qui ne te permet pas d'avoir
10 chiffres significatif. En bref, cette solution est à rejetée.

En fait, je voudrait
avoir une précision de 10 nombres après la virgule.

Si ce n'est pas trop compliqué, pouvez-vous me donner un exemple
simple ?.



Google devrait te fournir quelques lib de gestion de nombre décimaux
dans ton langage favoris (peut-être certains pourront te fournir de bon
lien; y'a ptêt ça dans boost puisque tu fais du C++), ou tu peux te
faire ta propre classe (tu te rappelles le "calcul écrit" qu'on nous
apprenait en primaire, et ben c'est le même concept, mais tu vas le
faire faire par ton ordi, en choisissant n'importe quelle structure
pour représenter ta suite de chiffre).


--
Quentin Pouplard (Tene/MyOE)
http://www.myoe.org | http://graff.alrj.org
Avatar
PendLoup
J'ai créer un projet Win32 Application sous Visual 6. Je n'ai rien changé
des options.
Puis j'ai ouvert ce projet sous Visual Net. A priori les options de
compilation sont restées les mêmes.

a+

Fred D.
Avatar
Christian ASTOR
PendLoup a écrit:


Dans le premier cas : double j = 0.1; donne 0.10000000000000001
alors qu'avec Visual 6 j'obtiens 0.1



J'en doute...

double j = 0.1;
printf("double=%.17f", j);

Et tape IEEE ds MSDN ou Google, il y a des tonnes d'articles expliquant
ce qu'a dit Quentin en long et en large..
Avatar
Cyrille \cns\ Szymanski
>> 2 solutions:
- si tu connais la précision nécessaire ("le nombre de chiffres après
la virgule") et qu'elle est constante, tu peux utiliser des virgules
fixes, pratiquement, tu manipules des int, en fixant arbitrairement
leur valeur. (note: attention au multiplication).



Je ne saisi pas tout à fait votre réponse ici. En fait, je voudrait
avoir une précision de 10 nombres après la virgule.



Ca me paraît ok vu que log2(10^10)3.22...

Il faut donc au moins 34 bits pour représenter un tel nombre. Un double
fera l'affaire vu que sa mantisse est stockée sur 52 bits.


Si ce n'est pas trop compliqué, pouvez-vous me donner un exemple
simple ?.




Utiliser double et faire un arrondi à 10^-10 près.


--
_|_|_| CnS
_|_| for(n=0;b;n++)
_| b&=b-1; /*pp.47 K&R*/
Avatar
Christian ASTOR
PendLoup a écrit:


Pour voir ce résultat j'ai juste posé un point d'arrêt après cette ligne et
j'ai visualisé j dans visual.
J'ai bien 2 résultats différents.



Parce que tu ne vois pas la véritable valeur ds VC++ 6
1 2