web-dev-qa-db-fra.com

Pourquoi double en C imprime-t-il moins de chiffres décimaux que C ++?

J'ai ce code en C où j'ai déclaré 0,1 comme double.

#include <stdio.h> 
int main() {
    double a = 0.1;

    printf("a is %0.56f\n", a);
    return 0;
}

C'est ce que ça affiche, a is 0.10000000000000001000000000000000000000000000000000000000

Même code en C++,

#include <iostream>
using namespace std;
int main() {
    double a = 0.1;

    printf("a is %0.56f\n", a);
    return 0;
}

C'est ce que ça affiche, a is 0.1000000000000000055511151231257827021181583404541015625

Quelle est la différence? Quand je lis les deux sont alloués 8 octets? Comment C++ imprime-t-il plus de nombres dans les décimales?

Aussi, comment peut-il aller jusqu'à 55 décimales? La virgule flottante IEEE 754 ne comporte que 52 bits pour les nombres fractionnaires, ce qui permet d'obtenir une précision de 15 chiffres décimaux. Il est stocké en binaire. Comment se fait-il que son interprétation décimale stocke plus?

63

Avec MinGW g ++ (et gcc) 7.3.0, vos résultats sont reproduits exactement.

C'est un cas assez étrange de comportement indéfini.

Le comportement indéfini est dû à l’utilisation de printf sans inclure d’en-tête approprié.

C++ 17 §20.5.2.2

Une unité de traduction ne doit comporter un en-tête qu'en dehors de toute déclaration ou définition, et doit inclure l'en-tête lexicalement avant la première référence dans cette unité de traduction des entités déclarées dans cet en-tête. Aucun diagnostic n'est requis.

Dans le code C++, changez _<iostream>_ en _<stdio.h>_ pour obtenir un code C++ valide et vous obtenez le même résultat qu'avec le programme C.


Pourquoi le code C++ est-il même compilé?

Contrairement à C, en C++, un en-tête de bibliothèque standard est autorisé à glisser dans n’importe quel autre en-tête. Et évidemment avec g ++ l'en-tête _<iostream>_ traîne dans une déclaration de printf. Pas tout à fait correct.

Détails: Avec MinGW g ++ 7.3.0, la déclaration/définition de printf dépend du symbole de la macro __USE_MINGW_ANSI_STDIO. La valeur par défaut est simplement que _<stdio.h>_ déclare printf. Mais lorsque ___USE_MINGW_ANSI_STDIO_ est défini comme étant logique vrai, _<stdio.h>_ fournit une définition prépondérante de printf, qui appelle ___mingw_vprintf_. Et comme cela arrive, l'en-tête _<cstdio>_ définit (via un include indirect) ___USE_MINGW_ANSI_STDIO_ avant d'inclure _<stdio.h>_.

Il y a un commentaire dans _<_mingw.h>_, "Notez que nous l'activons aussi pour _GNU_SOURCE en C++, mais pas pour le cas C.".

En C++, avec les versions pertinentes de ce compilateur, il existe une différence entre inclure _<stdio.h>_ et utiliser printf, ou inclure _<cstdio>_, dire _using std::printf;_ et utiliser printf.


En ce qui concerne

Aussi, comment peut-il aller jusqu'à 55 décimales? La virgule flottante IEEE 754 ne comporte que 52 bits pour les nombres fractionnaires, ce qui permet d'obtenir une précision de 15 chiffres décimaux. Il est stocké en binaire. Comment se fait-il que son interprétation décimale stocke plus?

... c'est juste la présentation décimale qui est plus longue. Les chiffres dépassant la précision de la représentation interne, environ 15 chiffres pour IEEE 754 64 bits, sont essentiellement des ordures, mais ils peuvent être utilisés pour reconstituer exactement les bits d'origine. À un moment donné, ils deviennent tous des zéros et ce point est atteint pour le dernier chiffre de la sortie de votre programme C++.


1Merci à Dietrich Epp pour avoir trouvé ce devis.

81

Il me semble que les deux cas affichent 56 chiffres décimaux. La question est donc techniquement fondée sur un principe erroné.

Je vois aussi que les deux nombres sont égaux à 0.1 à moins de 52 bits de précision, les deux sont donc corrects.

Cela nous amène à votre dernière question: "Comment se fait-il que son interprétation décimale stocke davantage?". Cela ne fait pas stocker plus de décimales. double ne stocke aucune décimale. Il stocke des bits. Les décimales sont générées.

10
MSalters