OVH Cloud OVH Cloud

Division et arrondi

54 réponses
Avatar
lhommedumatch
J'ai ce programme:
#include <iostream>
#include <math.h>

int main()
{
float x=3D10.0;
float y=3D1.0;
float scale=3D0.1;
int res;

res =3D int (x/scale);
std::cout << res << std::endl;
return 0;
}

Si je le compile avec:
version gcc 4.1.2 20071124 (Red Hat 4.1.2-42)
sur une machine 32bits j'obtiens 99
sur une machine 64bits j'obtiens 100

Quelqu'un pourrait m'=E9clairer.
Merci

10 réponses

1 2 3 4 5
Avatar
Vincent Lefevre
Dans l'article ,
Marc Boyer écrit:

En quelques lignes, on peut dire que les nombre flottant
représentent mal les puissances de 10 (puisqu'ils représentent les
nombres en puissance de deux),



C'est vrai en base 2. Pour info, l'arithmétique décimale est dans
la nouvelle norme IEEE 754-2008, et sera peut-être dans la prochaine
norme C, cf draft N1312 sur

http://www.open-std.org/jtc1/sc22/wg14/www/projects

Mais bon, ce n'est pas ça qui va résoudre tous les problèmes (avec
même un risque que ça crée de nouveaux problèmes).

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)
Avatar
Vincent Lefevre
Dans l'article ,
-ed- écrit:

On 25 mar, 12:14, lhommedumatch wrote:
> J'ai ce programme:
> #include <iostream>



C'est pas du C. Le C++, c'est à coté.



Mais il y a le même genre de problème en C.

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)
Avatar
Vincent Lefevre
Dans l'article ,
Kojak écrit:

Le Wed, 25 Mar 2009 04:14:47 -0700 (PDT),
lhommedumatch a écrit :



> J'ai ce programme:
> [...]
> float x.0;
> float scale=0.1;
> int res;
>
> res = int (x/scale);
> [...]
> version gcc 4.1.2 20071124 (Red Hat 4.1.2-42)
> sur une machine 32bits j'obtiens 99
> sur une machine 64bits j'obtiens 100



Essaye avec l'option "-ffloat-store", ça peut aider...



Attention, il faut alors stocker tous les résultats intermédiaires
dans des variables temporaires, e.g.

float x.0;
float scale=0.1;
float t;
int res;

t = x/scale;
res = (int) t;

Mais attention, ça ne règle pas le problème du double arrondi, donc
il peut y avoir encore des différences.

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)
Avatar
Antoine Leca
Le 26/03/2009 17:48Z, Vincent Lefevre écrivit :
Dans l'article ,
-ed- écrit:

On 25 mar, 12:14, lhommedumatch wrote:
J'ai ce programme:
#include <iostream>





C'est pas du C. Le C++, c'est à coté.



Mais il y a le même genre de problème en C.



Et en Fortran.
Et s'il écrivait en assembleur il pourrait aussi l'avoir (sauf que là,
il se rendrait immédiatement compte du pourquoi et du comment).


Antoine
Avatar
Antoine Leca
Le 25/03/2009 17:50, lhommedumatch écrivit :
La réponse ne tiendra pas en quelques lignes?



Imagine-toi qu'il y a encore dans ce XXIe siècle des gars qui font des
maîtrises voire des doctorats (Vincent tu me corriges/complètes s'il te
plaît) sur le sujet.

Et s'il est possible de résumer en quelques lignes un sujet de thèse...


Antoine
Avatar
Antoine Leca
Le 25/03/2009 21:03, Kojak écrivit :
Le Wed, 25 Mar 2009 04:14:47 -0700 (PDT),
lhommedumatch a écrit :

J'ai ce programme:
[...]
float x.0;
float scale=0.1;
int res;

res = int (x/scale);
[...]
version gcc 4.1.2 20071124 (Red Hat 4.1.2-42)
sur une machine 32bits j'obtiens 99
sur une machine 64bits j'obtiens 100



Essaye avec l'option "-ffloat-store", ça peut aider...



L'aider à quoi ?

C'est vrai que cela va forcer les deux machines à donner le même
résultat, mais il n'aura pas compris :
- pourquoi il a obtenu 99
- pourquoi il a obtenu 100
- pourquoi les deux résultats sont différents (OK, celle-là il devrait
pouvoir se débrouiller avec les explications données ici et en passant
un bon bout de temps à se documenter sur le sujet, soit à la
bibliothèque soit sur Google)
- pourquoi il ne faut pas écrire le code ci-dessus.

ÀMHA, le plus gros problème, c'est le dernier.


Antoine
Avatar
Antoine Leca
Le 25/03/2009 11:14Z, lhommedumatch écrivit :
float scale=0.1;
res = int (x/scale);
sur une machine 32bits j'obtiens 99
sur une machine 64bits j'obtiens 100

Quelqu'un pourrait m'éclairer.



24 est pair et 63 est impair (c'est à l'envers, hein).
Et 1/5 en base 2 a un motif de répétition de 2 bits.


Antoine
Avatar
Vincent Lefevre
Dans l'article <gqgfuk$tug$,
Antoine Leca écrit:

Le 25/03/2009 21:03, Kojak écrivit :
> Essaye avec l'option "-ffloat-store", ça peut aider...



L'aider à quoi ?



C'est vrai que cela va forcer les deux machines à donner le même
résultat, [...]



Même pas forcément, à cause du double arrondi possible (pas testé sur
le code en question). Avec les float, c'est cependant peu courant.
Mais entre la double précision et la précision étendue, avec certaines
hypothèses (bits aléatoires...), le double arrondi affecte le résultat
une fois sur 4096.

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)
Avatar
Vincent Lefevre
Dans l'article <gqgg2j$tug$,
Antoine Leca écrit:

Le 25/03/2009 11:14Z, lhommedumatch écrivit :
> float scale=0.1;
> res = int (x/scale);
> sur une machine 32bits j'obtiens 99
> sur une machine 64bits j'obtiens 100
>
> Quelqu'un pourrait m'éclairer.



24 est pair et 63 est impair (c'est à l'envers, hein).
Et 1/5 en base 2 a un motif de répétition de 2 bits.



Euh... pour la machine 64 bits (SSE2), 24, d'accord (c'est la simple
précision). Mais pour la machine 32 bits, la précision étendue x87,
c'est 64 bits. Mais si ce n'est pas une machine Linux, il est possible
que les calculs se fassent en double précision (53 bits).

D'autre part, il n'est pas clair si le 0.1 (double précision, sauf
éventuellement sous Linux, précision étendue) a été converti en float
(comme le veut la norme C) ou non (bug 323 de gcc).

Et en plus les optimisations du compilateur peuvent jouer.

Par exemple, sous Linux/x86:

ble:~> gcc tst.c -o tst
ble:~> ./tst
99
ble:~> gcc -O tst.c -o tst
ble:~> ./tst
100
ble:~> gcc tst.c -o tst -march=pentium4 -mfpmath=sse
ble:~> ./tst
100

mais les deux 100 sont peut-être obtenus pour des raisons complètement
différentes, quoique peut-être pas. Avec -O, le calcul est en fait ici
effectué à la compilation, mais je ne sais pas du tout ce que fait gcc
dans ce cas, et ça dépend peut-être de la version (MPFR est peut-être
aussi utilisé dans ce cas-là, ce qui garantirait le résultat "voulu",
tout comme l'utilisation de SSE).

Voilà donc une explication très partielle pour lhommedumatch, qui
voulait une réponse en quelques lignes... Et encore, je n'ai pas
parlé de contraction d'expressions flottantes[*].

[*] À ce propos, cf
http://www.vinc17.org/research/fptest.fr.html#contract2fma

Voilà, ce n'est pas tout, mais il faut que je termine de relire
le livre qu'on est en train d'écrire dans notre équipe, qui va
justement parler de tous ces problèmes et de plein d'autres choses
sur l'arithmétique virgule flottante.

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)
Avatar
Vincent Lefevre
Dans l'article <gqgg2j$tug$,
Antoine Leca écrit:

Le 25/03/2009 11:14Z, lhommedumatch écrivit :
> float scale=0.1;
> res = int (x/scale);
> sur une machine 32bits j'obtiens 99
> sur une machine 64bits j'obtiens 100
>
> Quelqu'un pourrait m'éclairer.



24 est pair et 63 est impair (c'est à l'envers, hein).
Et 1/5 en base 2 a un motif de répétition de 2 bits.



Euh... pour la machine 64 bits (SSE2), 24, d'accord (c'est la simple
précision). Mais pour la machine 32 bits, la précision étendue x87,
c'est 64 bits. Mais si ce n'est pas une machine Linux, il est possible
que les calculs se fassent en double précision (53 bits).

D'autre part, il n'est pas clair si le 0.1 (double précision, sauf
éventuellement sous Linux, précision étendue) a été converti en float
(comme le veut la norme C) ou non (bug 323 de gcc).

Et en plus les optimisations du compilateur peuvent jouer.

Par exemple, sous Linux/x86:

ble:~> gcc tst.c -o tst
ble:~> ./tst
99
ble:~> gcc -O tst.c -o tst
ble:~> ./tst
100
ble:~> gcc tst.c -o tst -march=pentium4 -mfpmath=sse
ble:~> ./tst
100

mais les deux 100 sont peut-être obtenus pour des raisons complètement
différentes, quoique peut-être pas. Avec -O, le calcul est en fait ici
effectué à la compilation, mais je ne sais pas du tout ce que fait gcc
dans ce cas, et ça dépend peut-être de la version (avec les versions
récentes de gcc, MPFR est peut-être aussi utilisé dans ce cas-là, ce
qui garantirait le résultat "voulu", tout comme l'utilisation de SSE).

Voilà donc une explication très partielle pour lhommedumatch, qui
voulait une réponse en quelques lignes... Et encore, je n'ai pas
parlé de contraction d'expressions flottantes[*].

[*] À ce propos, cf
http://www.vinc17.org/research/fptest.fr.html#contract2fma

Voilà, ce n'est pas tout, mais il faut que je termine de relire
le livre qu'on est en train d'écrire dans notre équipe, qui va
justement parler de tous ces problèmes et de plein d'autres choses
sur l'arithmétique virgule flottante.

--
Vincent Lefèvre - Web: <http://www.vinc17.org/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/>
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)
1 2 3 4 5