8
votes

Écrivez un flotteur avec une précision complète en C ++

en C ++, puis-je écrire et lire un float (ou double) dans le format de texte sans perdre de précision?

Considérez les suivants: P>

float f = ...;
{
    std::ofstream fout("file.txt");
    // Set some flags on fout
    fout << f;
 }
 float f_read;
 {
     std::ifstream fin("file.txt");
     fin >> f;
  }
  if (f != f_read) {
      std::cout << "precision lost" << std::endl;
  }


2 commentaires

Si vous voulez un stockage et un chargement précis, convertissez la représentation binaire du flotteur en base64 et stockez cela ...


"Cependant, si j'imprime la valeur avec suffisamment de chiffres, je devrais pouvoir lire exactement la même valeur." Oui, environ 17 chiffres décimaux significatifs. Question C connexes: Spécificateur de largeur d'impression pour maintenir une précision de la valeur de point flottante .


4 Réponses :


5
votes

Si j'imprime la valeur avec suffisamment de chiffres, je devrais pouvoir lire exactement la même valeur

Pas si vous l'écrivez en décimal - il n'y a pas une relation entière entre le nombre de chiffres binaires et le nombre de chiffres décimaux nécessaires pour représenter un nombre. Si vous imprimez votre numéro en binaire ou hexadécimal, vous pourrez le lire sans perdre de précision.

En général, les numéros de points flottants ne sont pas portables entre les plates-formes en premier lieu. Votre représentation de texte ne sera donc pas capable de combler cette lacune. En pratique, la plupart des machines utilisent des nombres de points flottants IEEE 754, de sorte qu'il fonctionnera probablement assez bien.


4 commentaires

Si j'ai suffisamment de chiffres décimaux, il devrait y avoir un seul flotteur qui est la représentation la plus proche, non?


@Luis, pas nécessairement - il peut être impossible pour vous de contrôler le comportement de l'arrondi, par exemple, donc si vous imprimez un nombre décimal et qu'il arrondit d'une manière, puis il arrondit une manière différente lorsque vous le lisez, vous ll être coincé.


C'était ma question: est-il possible de définir l'arrondi afin qu'il fonctionne simmétriquement?


@Luis - 100% compilateur et système à charge. Vous devrez aller lire votre documentation. Ce sera certainement non portable.



3
votes

Vous ne pouvez pas nécessairement imprimer la valeur exacte d'une "puissance de deux" flotteur en décimal.
Pensez à utiliser la base de trois à stocker 1/3, essayez maintenant d'imprimer 1/3 en décimale parfaitement.

pour les solutions Voir: Comment Imprimez-vous la valeur exacte d'un numéro de point flottant?


4 commentaires

C'est faux. Parce que 10 est un multiple de 2, chaque numéro de virgule flottante a une représentation finie en décimale (la converse n'est pas vraie).


@Stephen - Vous avez raison, apparemment IEEE754 exige que les règles d'arrondi génèrent le flottant résultant correct si vous utilisez exactement le nombre correct de décimales. Bien qu'ils ne le font pas si vous imprimez plus!


Je l'entendis en fait dans un sens purement mathématique. La situation est assez différente de l'écriture 1/3 en décimale, car 3 ne divise pas 10 uniformément. Ignorer IEEE754, chaque numéro rationnel dyadique peut être écrit avec un nombre fini de chiffres décimaux.


@Stephen - Oui c'était un mauvais exemple, je suis confus expliquant que vous ne pouviez pas nécessairement représenter une fraction dans une base finitive dans une autre - mais bien sûr, vous pouvez dans la base 2 et 10 (à l'exception de la règle arrondie sinistre le format)




6
votes

Si vous n'avez pas besoin de prendre en charge les plates-formes qui manquent de support C99 (MSVC), votre meilleur pari est en fait pour utiliser le spécificateur de format % A avec printf , qui génère toujours une représentation exacte (hexadécimale) du nombre tout en utilisant un nombre limité de chiffres. Si vous utilisez cette méthode, aucun arrondi n'a lieu pendant la conversion à une chaîne ou à l'arrière, le mode arrondi n'a donc aucun effet sur le résultat.


5 commentaires

S'il n'y a pas besoin de lecture humaine dans le fichier, imprimez simplement le numéro comme hexadimaux printf ("% 8x% 16x", 1.1f, 1.1); . C'est le plus rapide et fonctionne indépendamment du support C99


@ Lưuvĩnhphúc: deux choses: % A est lisible par l'homme et votre suggestion invoque un comportement indéfini (en particulier, il ne fera pas ce que vous voulez sur la plupart des plates-formes de 64 bits; il va simplement imprimer arbitraire. bits d'ordures).


Le comportement n'est pas indéfini car PrintF apparaît simplement 8 octets de Stack et décide de ce que le type de données dépend du formateur. Si vous utilisez% A, il traitera ces octets comme double,% LLD les traitera aussi longtemps que l'INT et de la même manière que% LLX les imprimera simplement comme hexadécimales. Le code que j'ai tapé ci-dessus a une erreur. Le droit doit être printf ("% 08llx% 016llx", 1.1f, 1.1); Une autre façon d'utiliser est printf ("% 08x \ n", * (int *) & a );


C'est ainsi que Printf travaille sur quelques plates-formes. Cependant, il n'y a rien à le garantir. Essayez osx ou Linux 64 bits et voyez ce qui se passe ...


@ Lưuvĩnhphúc "apparaît sur 8 octets de la pile et décide de savoir ce que les données" -> s'attendent à ce que les valeurs de la FP soient parfois passées dans une pile FP et des entiers dans la pile habituelle. printf ("% 8x% 16x", 1.1f, 1.1); reste ub.