OVH Cloud OVH Cloud

comparaison de flottants

209 réponses
Avatar
Emmanuel
bonjour tout le monde,

une question de vrai débutant :

Avec le programme essai.c suivant

int main(void) {
return ((1.7 + 0.1) == 1.8) ? 1 : 0;
}

j'obtiens :

$ gcc -Wall -o essai essai.c; ./essai; echo $?
1

Avec le programme essai.c suivant

int main(void) {
float x;
x = 1.7;
return ((x + 0.1) == 1.8) ? 1 : 0;
}

j'obtiens :

$ gcc -Wall -o essai essai.c; ./essai; echo $?
0

Pourtant, le programme

#include <stdio.h>
int main(void) {
float x;
x = 1.7;
printf("%f et %f\n", 1.7 + 0.1, x + 0.1);
}

affiche :
1.800000 et 1.800000

Apparremment, 1.7+0.1 et x+0.1 sont égaux ; j'imagine qu'il s'agit d'un
problème de représentation des flottants en machine mais j'aimerais bien
avoir une confirmation ou une infirmation (et une explication, si possible).

Merci par avance.

Emmanuel

10 réponses

Avatar
Samuel DEVULDER
Antoine Leca a écrit :

Il reste Microsoft qui eux sont restés sur long2 bits, tout en
expliquant qu'il ne faut pas utiliser les types de base (ni printf).



Oh il n'y a pas que MSVC qui a des long à 32bits.. TCC, et LCC sont
aussi comme cela. Il faudra que vérifie CodeWarrior mais ca ne
m'étonnerait pas non plus (et pourtant le CPU cible est un 64bits...)

Il y a quand même une sacrée base de compilos installés qui sont comme
ca je pense. Tout n'évolue pas super vite.

Par ailleurs, je trouve que dans des environnements évolutifs, c'est
effectivement un soucis d'utiliser les types de base dans la mesure où
rien de garanti la taille en bit. Les typedefs de <stdint.h> sont plutôt
une bonne façon de se prémunir du fait que la taille du long int varie
au cours du temps et entre les systèmes.

sam.
Avatar
Vincent Lefevre
Dans l'article <4bbb7cce$0$18404$,
Samuel DEVULDER écrit:

Ca veut dire quoi "sale" pour toi?



Utiliser des int implicites, par exemple.

Est-ce sale d'écrire "static a = 4" dans du code? Pas forcément. En C89
int est implicite et le code se comprend par tous.



Ben non, le code est peu lisible. Avoir un type fixé implicite est
complètement arbitraire. Par exemple, pourquoi int et pas double,
ou le type entier le plus long? Pourquoi pas le même type que celui
utilisé par le préprocesseur? Bref, cette notion de type implicite
était foireuse dès le départ.

Dialogue:

Perso1 => Oui mais l'implicite c'est pas-bien(tm)!
Perso2 => Ah ok, mais quand on écrit static long a = 4 en C99 c'est bien
alors?
Perso1 => Oui!
Perso2 => Mais le long n'est pas un type. Le vrai type c'est "long int".
"int" est encore implicite en C99.
Perso1 => Ah oui l'implicite c'est propre alors!



Faux, long est un véritable type (6.7.2). Personnellement, je trouve
que la norme n'aurait dû avoir que des types de base sous forme d'un
seul token, mais bon...

> Du code C89 sale... D'ailleurs, certains exemples sont mauvais même
> en C99, e.g. oubli du "return 0;".

même en C99? Oui surtout en C99! parce qu'en C89 ca passe, cf:
f() {... blabla; if(truc) return; blabla...}



C'est une erreur de conception qui a été corrigée. Ça compile
peut-être, mais à l'exécution, ça bugge: on récupère une valeur
de retour arbitraire (tout du moins avec certains compilateurs).
Pour main(), c'est la commande qui peut échouer sous Unix. Bref,
un gros bordel.

Grossièrement pour ceux qui ne savent plus lire du vieux C: f est une
fonction qui retourne rien, d'où return vide. Logique. Sauf que rien
signifie implicitement int en C89. En pratique ca veut dire que le
compilo ne retourne rien de spécial à la fonction. Il va peut être
utiliser cela pour optimiser. Qui sait?



Si on ne s'intéresse pas à une valeur de retour, autant définir une
fonction void f(...) { ... }.

>> Autre truc. C99 est plus stricte que C89. Normalement C89 laisse passer
>> ce qui suit, mais pas C99:
>> char *s=...
>> unsigned char *t=s; /* erreur avec C99 */
>
> Ça me semble valide en C99. Pourquoi ne le serait-ce pas?

==> pointer targets in initialization differ in signedness.



OK, je suppose qu'il s'agit du 6.7p4.

Mais de toute façon, aucun changement par rapport à C89, d'après GCC:

ypig:~> gcc-snapshot -c tst.c -stdÈ9 -pedantic
tst.c: In function 'foo':
tst.c:4:22: warning: pointer targets in initialization differ in signedness

ypig:~> gcc-snapshot -c tst.c -stdÉ9 -pedantic
tst.c: In function 'foo':
tst.c:4:22: warning: pointer targets in initialization differ in signedness

>>> Quand on ne précise pas le contexte, c'est celui de la norme C ISO.
>>> La seule norme C est actuellement la version de 1999 (a.k.a. C99).
>>> C89 n'est plus une norme, même si on peut en parler...
>
>> Pourquoi ?
>
> Parce que C89 n'est plus une norme.

Pourquoi ca n'est pas une norme? Parce que ca n'est plus une norme, pardi!



Pfff... Tu devrais signaler sur quoi porte ton "pourquoi".

Concernant C99 vs C89: "This second edition cancels and replaces the
first edition".

--
Vincent Lefèvre - Web: <http://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / Arénaire project (LIP, ENS-Lyon)
Avatar
Vincent Lefevre
Dans l'article <4bbb724f$0$7072$,
Samuel DEVULDER écrit:

Le problème entre Vincent et moi sur ce truc c'est que quand on parle
des nombres qui suivent le "p", il dit binaire par rapport au fait que
p=2^d ce que je juge inutilement précis/imprécis car
- p est en lui même "puissance de deux"
- ca ne dit pas comment interpréter les nombres suivant: en base dix,
deux ou seize? (16 serait logique par référence à la fraction à ce
moment là)



La bonne formulation serait: "exposant binaire écrit en base 10".

Note que cela suivrait aussi l'usage mathématique, où on parle
de "logarithme décimal", par exemple: cela ne signifie pas qu'on
écrit le résultat en base 10, mais que c'est la fonction
réciproque de 10^x. Le "logarithme binaire" est également
utilisé, et c'est la fonction réciproque de 2^x, et non pas
un logarithme écrit en base 2.

--
Vincent Lefèvre - Web: <http://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / Arénaire project (LIP, ENS-Lyon)
Avatar
Vincent Lefevre
Dans l'article <4bbb6912$0$16031$,
Samuel DEVULDER écrit:

Vincent Lefevre a écrit :
> Dans l'article <4bbb03ab$0$8366$,
> Samuel DEVULDER écrit:
>
>> Désolé. Sur http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf je
>> n'ai pas trouvé "binary exponent part" dans la description de printf
>
> Ce n'était pas dans la description de printf (le format en question
> revient à plusieurs endroits dans la norme, pas seulement avec
> printf).

Heu je sais pas.. on parle de printf, aussi je regarde la spec de ce
dernier? Pas toi? Tu vas voir de quel coté toi, strod? atoi? rand?
Quelle blague!



Tu demandais ce que voulait dire "exposant binaire". Je te donne
simplement une référence où ce terme est employé! Et quitte à
utiliser une spec, autant choisir celle qui utilise les termes
adéquats.

Si tu ne veux pas qu'on réponde à tes questions, n'en pose pas!

--
Vincent Lefèvre - Web: <http://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / Arénaire project (LIP, ENS-Lyon)
Avatar
Vincent Lefevre
Dans l'article <4bbbd825$0$24902$,
Samuel DEVULDER écrit:

Oh il n'y a pas que MSVC qui a des long à 32bits.. TCC, et LCC sont
aussi comme cela. Il faudra que vérifie CodeWarrior mais ca ne
m'étonnerait pas non plus (et pourtant le CPU cible est un 64bits...)



Pas essayé LCC, mais pour TCC, c'est faux. Sous Linux/x86_64:

ypig:.../testcases/c-impl> tcc types.c
ypig:.../testcases/c-impl> ./a.out
[...]
--> long / unsigned long
LONG_MIN = - 2^63 = -9223372036854775808
LONG_MAX = 2^63 - 1 = 9223372036854775807
ULONG_MAX = 2^64 - 1 = 18446744073709551615
sizeof(long) = 8, signed
sizeof(unsigned long) = 8, unsigned
[...]

--
Vincent Lefèvre - Web: <http://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / Arénaire project (LIP, ENS-Lyon)
Avatar
Samuel DEVULDER
Vincent Lefevre a écrit :
Dans l'article <4bbb6912$0$16031$,
Samuel DEVULDER écrit:

Vincent Lefevre a écrit :
Dans l'article <4bbb03ab$0$8366$,
Samuel DEVULDER écrit:

Désolé. Sur http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf je
n'ai pas trouvé "binary exponent part" dans la description de printf


Ce n'était pas dans la description de printf (le format en question
revient à plusieurs endroits dans la norme, pas seulement avec
printf).





Heu je sais pas.. on parle de printf, aussi je regarde la spec de ce
dernier? Pas toi? Tu vas voir de quel coté toi, strod? atoi? rand?
Quelle blague!



Tu demandais ce que voulait dire "exposant binaire". Je te donne



Non.

simplement une référence où ce terme est employé! Et quitte à
utiliser une spec, autant choisir celle qui utilise les termes
adéquats.



Tu as loupé aussi une référence de "binary exponent" dans la spec
fortran 90 alors aussi pendant qu'on y est. Quand on te demande où
comment on se rend à la rue de la poste à Albi, toi tu réponds au sujet
de la rue de la poste à Bordeaux. Super!

Si tu ne veux pas qu'on réponde à tes questions, n'en pose pas!



Hum... Ridicule.

sam.
Avatar
Samuel DEVULDER
Vincent Lefevre a écrit :
Dans l'article <4bbb7cce$0$18404$,
Samuel DEVULDER écrit:

Ca veut dire quoi "sale" pour toi?



Utiliser des int implicites, par exemple.



Pourquoi? Parce que C99 l'interdit? On te parles justement de regarder
ce qui se faisait avant. Encore une fois tu te plantes d'endroit. T'es
coincé rue de la porte à bordeaux quand on t'a donné rendez-vous rue de
la poste à Albi.

sam.
Avatar
Samuel DEVULDER
Vincent Lefevre a écrit :
Dans l'article <4bbb724f$0$7072$,
Samuel DEVULDER écrit:

Le problème entre Vincent et moi sur ce truc c'est que quand on parle
des nombres qui suivent le "p", il dit binaire par rapport au fait que
p=2^d ce que je juge inutilement précis/imprécis car
- p est en lui même "puissance de deux"
- ca ne dit pas comment interpréter les nombres suivant: en base dix,
deux ou seize? (16 serait logique par référence à la fraction à ce
moment là)



La bonne formulation serait: "exposant binaire écrit en base 10".



Mais oui mais oui... et je dirais même plus. C'est une simple notation
décimale binaire pendant qu'on y est. Rien de plus simple. Dingue! On
attends tous la révision *Vincent* de la norme.

Note que cela suivrait aussi l'usage mathématique, où on parle
de "logarithme décimal", par exemple: cela ne signifie pas qu'on
écrit le résultat en base 10, mais que c'est la fonction
réciproque de 10^x. Le "logarithme binaire" est également
utilisé, et c'est la fonction réciproque de 2^x, et non pas
un logarithme écrit en base 2.



Hum.. quel rapport avec printf? Tu nous parles de plein de trucs forts
(in)utiles, mais ca ne figure absolument pas dans la doc du printf %a?
Dis, tu serais en train de noyer le poisson?

sam.
Avatar
Samuel DEVULDER
Vincent Lefevre a écrit :
Dans l'article <4bbbd825$0$24902$,
Samuel DEVULDER écrit:

Oh il n'y a pas que MSVC qui a des long à 32bits.. TCC, et LCC sont
aussi comme cela. Il faudra que vérifie CodeWarrior mais ca ne
m'étonnerait pas non plus (et pourtant le CPU cible est un 64bits...)



Pas essayé LCC, mais pour TCC, c'est faux. Sous Linux/x86_64:

ypig:.../testcases/c-impl> tcc types.c
ypig:.../testcases/c-impl> ./a.out
[...]
--> long / unsigned long
LONG_MIN = - 2^63 = -9223372036854775808
LONG_MAX = 2^63 - 1 = 9223372036854775807
ULONG_MAX = 2^64 - 1 = 18446744073709551615
sizeof(long) = 8, signed
sizeof(unsigned long) = 8, unsigned
[...]



Et alors? TCC sous DOS, ou ARM a aussi des longs à 32bit et c'est pour
cela que je dis que MSVC n'est pas le seul à avoir un long à 32bit.

Pour ce qui seraient intéressés par apprendre des trucs et pas essayer
de donner des leçon, TCC est un compilo C tout petit, quasi fidèle à
C99, compatible gcc (y compris ses extensions), capable de compiler le
noyau linux et booter dessus en quelques secondes.
http://bellard.org/tcc/
Dans ce compilo on trouve:

/* return 0 if no type declaration. otherwise, return the basic type
and skip it.
*/
static int parse_btype(CType *type, AttributeDef *ad)
{
/** cut ../.. */
/* long is never used as type */
if ((t & VT_BTYPE) == VT_LONG)
#ifndef TCC_TARGET_X86_64
t = (t & ~VT_BTYPE) | VT_INT;
#else
t = (t & ~VT_BTYPE) | VT_LLONG;
#endif
type->t = t;
return type_found;
}
ce qu'il se passe, dans cette partie c'est on remplace "long" par un int
ou long long (64bits). Pour la target X86_64 (linux), c'est 64bits et
*partout* ailleurs (dos, arm, C67, cil) c'est du 32bits.

Pour l'instant les unixoides ont fait la bascule sizeof(long)==8, le
reste du monde (et il y en a pas mal) ont encore une ABI assez ancrée
32bits.


sam
Avatar
Vincent Lefevre
Dans l'article <4bbc320c$0$22387$,
Samuel DEVULDER écrit:

Vincent Lefevre a écrit :
> Pas essayé LCC, mais pour TCC, c'est faux. Sous Linux/x86_64:
>
> ypig:.../testcases/c-impl> tcc types.c
> ypig:.../testcases/c-impl> ./a.out
> [...]
> --> long / unsigned long
> LONG_MIN = - 2^63 = -9223372036854775808
> LONG_MAX = 2^63 - 1 = 9223372036854775807
> ULONG_MAX = 2^64 - 1 = 18446744073709551615
> sizeof(long) = 8, signed
> sizeof(unsigned long) = 8, unsigned
> [...]

Et alors? TCC sous DOS, ou ARM a aussi des longs à 32bit et c'est pour
cela que je dis que MSVC n'est pas le seul à avoir un long à 32bit.



ARM n'est pas un processeur 64 bits!!!

[...]
ce qu'il se passe, dans cette partie c'est on remplace "long" par un int
ou long long (64bits). Pour la target X86_64 (linux), c'est 64bits et
*partout* ailleurs (dos, arm, C67, cil) c'est du 32bits.



Parce que pour le reste, il ne s'agit pas de processeurs 64 bits.

Pour l'instant les unixoides ont fait la bascule sizeof(long)==8, le
reste du monde (et il y en a pas mal) ont encore une ABI assez ancrée
32bits.



Mais à par les Unix (incluant Mac OS X) et MS Windows, qu'y a-t-il
tournant sur processeur 64 bits?

À noter qu'un long sur 32 bits introduit des limitations, par exemple
sur la taille des fichiers. Cf les fonctions standard qui utilisent
des long, e.g. fseek et ftell. Il est toujours possible d'utiliser des
fonctions non standard, mais les programmes portables seront limités à
des fichiers sur 2 Go (ou trucs de ce style) sur de telles architectures.

--
Vincent Lefèvre - Web: <http://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <http://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / Arénaire project (LIP, ENS-Lyon)