gcc, printf et long double

Le
Pierre Maurette
Bonjour,

J'ai un souci pour printf-er des long double. Avec gcc 4.1 (Ubuntu
AMD64), pas de problème avec:
printf("%Lf", x);

En revanche sous Windows XP x64, gcc 3.4.5, pour afficher quelque chose
qui au moins me montre que le calcul s'est fait, je dois faire:
printf("%Lf", (double)x);
J'ai positionné -stdÉ9

Pourtant la norme est claire. Sans parler de bug, s'agit-il d'un truc
connu, une implémentation foireuse de C99 dans la version 3.4.5 ?

Dans ces conditions, et si je souhaite rester avec cette version, y
a-t-il une solution autre que le cast en double ?

Merci d'avance et bonne journée

--
Pierre Maurette
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Vincent Lefevre
Le #1002591
Dans l'article Pierre Maurette
Bonjour,

J'ai un souci pour printf-er des long double.


Quel souci exactement? Erreur à la compilation? Plantage? Perte de
précision?

Avec gcc 4.1 (Ubuntu
AMD64), pas de problème avec:
printf("%Lfn", x);

En revanche sous Windows XP x64, gcc 3.4.5, pour afficher quelque chose
qui au moins me montre que le calcul s'est fait, je dois faire:
printf("%Lfn", (double)x);
J'ai positionné -stdÉ9


Si tu castes en double, ce n'est plus un long double.

D'autre part, avec le FPU traditionnel des processeurs x86, la précision
est dynamique, ce qui pose des problèmes en pratique, surtout avec gcc,
qui fait n'importe quoi (mais je ne sais pas si les autres compilateurs
sont mieux).

Pourtant la norme est claire.


Mais la norme n'est pas faite pour les précisions dynamiques. Il y a
tout de même divers moyen de respecter la norme, mais gcc ne le fait
pas.

Sans parler de bug, s'agit-il d'un truc connu, une implémentation
foireuse de C99 dans la version 3.4.5 ?


Même dans les versions 4.x, c'est foireux.

Avec AMD64, c'est SSE2 qui est utilisé par défaut, il n'y a donc pas
de problème.

Dans ces conditions, et si je souhaite rester avec cette version, y
a-t-il une solution autre que le cast en double ?


Je n'ai pas compris l'histoire de ce cast. Note: Windows est en double
précision par défaut, donc on devrait avoir long double = double sous
Windows.

--
Vincent Lefèvre 100% accessible validated (X)HTML - Blog: Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)

Pierre Maurette
Le #1002590
Dans l'article Pierre Maurette
Bonjour,

J'ai un souci pour printf-er des long double.


Quel souci exactement? Erreur à la compilation? Plantage? Perte de
précision?


Affiche n'importe quoi, ou plutôt n'importe quel flottant. Soit
-0.000000, soit un très grand négatif. Certainement un type lu à
l'adresse d'un autre, je n'ai pas enquêté dans le listing asm.

Pour le reste, le long double est en machine sur 80 bits. Son sizeof
fait 8 chez Microsoft, pour des raisons de compatibilté (après avoir
fait plus sur des versions antérieures). Il fait 12 (donc padding) chez
Gcc, pour des raisons soit de compatibilité, soit d'alignement, je n'en
sais rien. Il me semble qu'il fait (faisait ?) 10 chez Borland.

Si je fais tourner:

long double x = 1.2;
printf("%lun", (unsigned long)sizeof(double));
printf("%lun", (unsigned long)sizeof(long double));
printf("%Lfn", x);
printf("%fn", x);
printf("%Lfn", (double)x);
printf("%fn", (double)x);

J'ai tout bon chez Microsoft, et pas de warning (puisque %Lf accepte
bien sûr aussi bien double que long double)

En gcc 3.4.5, j'ai logiquement deux warnings "long double format,
double arg", et en sortie:
8
12
-0.000000
-0.000000
1.200000
1.200000

Sous Linux, et gcc 4.1, tout va bien.

--
Pierre Maurette


Vincent Lefevre
Le #1002589
Dans l'article Pierre Maurette
Dans l'article Pierre Maurette
Bonjour,

J'ai un souci pour printf-er des long double.


Quel souci exactement? Erreur à la compilation? Plantage? Perte de
précision?


Affiche n'importe quoi, ou plutôt n'importe quel flottant. Soit
-0.000000, soit un très grand négatif. Certainement un type lu à
l'adresse d'un autre, je n'ai pas enquêté dans le listing asm.


Tu as bien mis un #include d'absence de prototype, mais si le cast en double résout le problème,
je doute que ce soit la raison (car il devrait y avoir le même problème
avec le type double).

Maintenant, le fait que le cast en double résolve le problème suggère
plutôt une incompatibilité entre le compilateur et la bibliothèque
conceernant le type long double, la bibliothèque s'attendant peut-être
à ce que double et long double aient le même format. Cela expliquerait
le comportement observé.

Pour le reste, le long double est en machine sur 80 bits. Son sizeof
fait 8 chez Microsoft, pour des raisons de compatibilté (après avoir
fait plus sur des versions antérieures).


Oui, Windows est passé à la double précision par défaut. Cela résout un
certain nombre de problèmes:

http://www.vinc17.org/research/extended.fr.html

Il fait 12 (donc padding) chez Gcc, pour des raisons soit de
compatibilité, soit d'alignement, je n'en sais rien. Il me semble
qu'il fait (faisait ?) 10 chez Borland.


Je pense que c'est le problème que je mentionne ci-dessus. Gcc fait
comme si long double = 3 mots de 32 bits, mais la bibliothèque, prévue
pour le compilateur de Microsoft, s'attend à 2 mots de 32 bits (comme
pour le type double).

--
Vincent Lefèvre 100% accessible validated (X)HTML - Blog: Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)



jacob navia
Le #1000689
Bonjour,

J'ai un souci pour printf-er des long double. Avec gcc 4.1 (Ubuntu
AMD64), pas de problème avec:
printf("%Lfn", x);

En revanche sous Windows XP x64, gcc 3.4.5, pour afficher quelque chose
qui au moins me montre que le calcul s'est fait, je dois faire:
printf("%Lfn", (double)x);
J'ai positionné -stdÉ9

Pourtant la norme est claire. Sans parler de bug, s'agit-il d'un truc
connu, une implémentation foireuse de C99 dans la version 3.4.5 ?

Dans ces conditions, et si je souhaite rester avec cette version, y
a-t-il une solution autre que le cast en double ?

Merci d'avance et bonne journée...



Salut Pierre

C'est mort, puisque gcc+mingw utilisent crtdll.dll, le run time
de windows, et les long doubles Microsoft connait pas.

Utilise plutot lcc-win32

http://www.cs.virginia.edu/~lcc-win32

Le seul compilateur C construit dans le 93!

jacob

Pierre Maurette
Le #1000688

[...]

Salut Pierre


Bonsoir

C'est mort, puisque gcc+mingw utilisent crtdll.dll, le run time
de windows, et les long doubles Microsoft connait pas.


Ou msvcrt.dll (sur les versions actuelles ?). Les symptomes étaient
clairs, mais je confusais mentalement, et que crois que j'avais déjà
été corrigé ici sur ce point. J'avais dans l'idée que pour des
problèmes de droits, mingw *n'utilisait pas* les runtime Microsoft. Ce
qui je l'admets ne résistait pas à un début de réflexion.

Utilise plutot lcc-win32

http://www.cs.virginia.edu/~lcc-win32

Le seul compilateur C construit dans le 93!


J'avais déjà regardé. Mais il ne semble pas que ce soit possible
simplement de continuer à utiliser Eclipse ou Code::Blocks.

--
Pierre Maurette

Antoine Leca
Le #1000687
En news:,
Pierre Maurette va escriure:
J'ai un souci pour printf-er des long double.




Contexte resuscité d'un message antérieur
:>> En revanche sous Windows XP x64, gcc 3.4.5, pour afficher quelque
:>> chose qui au moins me montre que le calcul s'est fait, je dois faire:
:>> printf("%Lfn", (double)x);
:>> J'ai positionné -stdÉ9

C'est bien tout cela, mais comme printf fait partie de la bibliothèque
standard, et que gcc est techniquement un compilateur « freestanding », il
faut préciser avec quelle bibliothèque standard tu lies...
Surtout qu'en l'occurence, suivant si tu utilises Cygwin1.dll, MSVCRT.DLL
direct, ou encore Machin.lib interposée + MSVCRT (cf. Jacob), ou bien
LIBC.lib de Microsoft, tu vas avoir des résultats différents...


Quel souci exactement? Erreur à la compilation? Plantage? Perte de
précision?


Affiche n'importe quoi, ou plutôt n'importe quel flottant. Soit
-0.000000, soit un très grand négatif. Certainement un type lu à
l'adresse d'un autre, je n'ai pas enquêté dans le listing asm.


Mmmmmoui. Le plus probable, le compilateur est réglé (compilé si tu
préfères) avec des long double de 12 octets (suffit de faire sizeof pour le
savoir), tandis que la bibliothèque (statique ou dynamique) qui contient le
_doprintf() utilisé a été compilé avec des long double de 8 octets...

Déjà, GCC 3.4.5 capable de compiler proprement pour Win64-x64, c'est nouveau
pour moi (et aussi pour gcc.org, cf.
http://gcc.gnu.org/gcc-3.4/buildstat.html). Tu dois utiliser un build récent
de binutils, non ? Ou bien tu es en mode i386 ?

Et puis on peut aussi lire avec profit (?)
http://www.mingw.org/MinGWiki/index.php/long%20double


Pour le reste, le long double est en machine sur 80 bits.


Si tu vas par là, le float aussi est en 80 bits ; mais tout le monde s'en
moque, ou presque. Ce qui importe, c'est comment il est stocké en mémoire,
et en particulier dans la pile au moment du passage de paramètres.

Son sizeof fait 8 chez Microsoft, pour des raisons de compatibilté


... avec la plateforme MIPS, oui.

(après avoir fait plus sur des versions antérieures).


Il y a longtemps ; la dernière version, ce devait être C8.02 a.k.a. VC+ 1.5
pour Windows 16 bits. 1993.


Il fait 12 (donc padding) chez Gcc, pour des raisons soit de
compatibilité, soit d'alignement, je n'en sais rien.


ÀMHA, il /devrait/ y avoir des options de compilation de GCC qui permettent
de choisir d'avoir soit des long double sur 64 bits,
peut-être -m64bit-long-double ?; sachant que pour les long double sur 80
bits, on peut forcer le stockage dans 12 octets (naturel en i386)
avec -m96bit-long-double; ou 16 octets (standard en x86-64 pour respecter un
alignement « naturel » des accès par le processeur)
avec -m128bit-long-double.

Ou alors, /ils/ (La Cabale, pas GCCSC, bien sûr) l'ont fait exprès...


Il me semble qu'il fait (faisait ?) 10 chez Borland.


En 32 bits ? surprenant, vu la pénalité que cela génère.
En 16 bits, bien sûr, la plupart des compilos pour MS-DOS ont choisit
d'avoir des long double sur 80 bits, pour exploiter l'avantage marketing de
la « précision des calculs ». Mais c'était il y a longtemps.


Si je fais tourner:

long double x = 1.2;
printf("%lun", (unsigned long)sizeof(double));
printf("%lun", (unsigned long)sizeof(long double));
printf("%Lfn", x);
printf("%fn", x);
printf("%Lfn", (double)x);
printf("%fn", (double)x);

J'ai tout bon chez Microsoft, et pas de warning (puisque %Lf accepte
bien sûr aussi bien double que long double)


Même si la taille était la même, gcc émettrait un avertissement, puisque tu
mélanges deux types, donc tu as un problème potentiel de portabilité.


Antoine



Publicité
Poster une réponse
Anonyme