Comment C peut-il imprimer un très grand nombre?

Comment ce code peut-il imprimer un si grand nombre? Je l’ai essayé sur Ubuntu 14.04 (gcc 4.8.2). Il ne fonctionne pas de la même manière sous MS Windows avec aucun compilateur (même MinGW, appelé “gcc pour Windows”). Pourquoi?

#include  #include  int main() { printf("%.0f\n",pow(2,500)); } 

Sortie Ubuntu:

 3273390607896141870013189696827599152216642046043064789483291368096133796404 674554883270092325904157150886684127560071009217256545885393053328527589376 

Sortie Windows:

 3273390607896141900000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000 

(Le saut de ligne est ajouté pour plus de clarté seulement.)

Comme indiqué par OP et commenté par @ user300234, 2^500 est un numéro à 501 bits, mais ce n’est pas le problème ici.

pow(2,500) renvoie un double , environ 3.27e150 , qui est généralement un binary64 . Ce type prend en charge environ 15 à 17 chiffres décimaux de précision. Donc, pour les nombres proches de 3.27e150 , imprimer plus de 17 chiffres significatifs n’est généralement pas important. Il est bien plus petit que DBL_MAX qui peut être environ 1.798e308 .

L’astuce ici est que pow(2,500) est exactement représentable sous forme de double virgule flottante (binary64). Cela peut donner l’illusion qu’un double a des centaines de bits de précision – ce n’est pas le cas.

Les 2 différentes compilations gèrent la conversion du double en texte de différentes manières, une fois que quelque 17 chiffres ont été imprimés – ceci est permis par la spécification C. Le nombre minimum de chiffres corrects est DBL_DECIMAL_DIG – probablement 17 sur les deux systèmes.

Envisagez d’imprimer le double supérieur suivant . Bien que le double suivant puisse être imprimé avec environ 150 chiffres, généralement ces deux chiffres supplémentaires passent par DBL_DECIMAL_DIG sont simplement du bruit pour de nombreuses applications.

 // 2 ^ 500 327339060789614 187001318969682759915221664... // next 327339060789614 259685191399243448970154045... 

La bibliothèque d’ msvcrt.dll Microsoft Visual C ( msvcrt.dll ), également utilisée par MinGW, ne prend en charge que 17 chiffres de précision, car cela suffit à identifier de manière unique un double (IEEE 754).

Mais bonne nouvelle: votre code compilé avec VS2015 CTP4 produit le même résultat que votre exemple Ubuntu. Et si vous utilisez MinGW-w64, vous pouvez définir __USE_MINGW_ANSI_STDIO comme 1 avant d’inclure ou via l’option du compilateur -D pour la même sortie.

Ce n’est pas une nouvelle réponse, mais un tour légèrement différent de ce que d’autres ont déjà dit:

La différence n’est pas dans la fonction pow() : la différence réside dans la fonction printf() . Ubuntu printf () imprime la valeur décimale exacte de pow(2,500) . Windows printf () imprime une approximation correcte à 17 chiffres décimaux.

Les deux implémentations sont, dans un certain sens, correctes; mais je dirais que Windows printf () est plus correct. Voici pourquoi:

Si vous vous souciez de plus de 17 chiffres de précision, vous ne devriez pas utiliser le double .

En général, le résultat de la plupart des calculs doubles n’est correct que dans les 17 premiers chiffres. ( pow(2, n) est un cas très particulier où la réponse est exactement correcte pour toute plage de n ). En imprimant la valeur décimale exacte de très grands doubles, la bibliothèque Ubuntu vous trompe; Supposons que vous ayez

 double a = ...; double b = ...; double c = a*b; printf("%f", c); 

Vous ne voulez pas connaître la valeur exacte de c car, pour la plupart des a et b , c n’est pas égal à a*b : ce n’est qu’une approximation de * b – une approximation précise à environ 17 chiffres significatifs.

Si vous avez besoin de plus de 17 chiffres de précision, vous devriez utiliser une bibliothèque mathématique de précision étendue telle que GMP ( https://gmplib.org/ )