web-dev-qa-db-fra.com

Toutes les valeurs entières sont-elles parfaitement représentées comme des doubles?

Ma question est de savoir si toutes les valeurs entières sont garanties d'avoir une double représentation parfaite.

Considérez l'exemple de code suivant qui imprime "Identique":

// Example program
#include <iostream>
#include <string>

int main()
{
  int a = 3;
  int b = 4;
  double d_a(a);
  double d_b(b);

  double int_sum = a + b;
  double d_sum = d_a + d_b;

  if (double(int_sum) == d_sum)
  {
      std::cout << "Same" << std::endl;
  }
}

Est-ce garanti pour toute architecture, tout compilateur, toute valeur de a et b? Tout entier i converti en double, sera-t-il toujours représenté par i.0000000000000 et non, par exemple, comme i.000000000001?

Je l'ai essayé pour d'autres numéros et c'était toujours vrai, mais je n'ai pas pu trouver quoi que ce soit par hasard ou par conception.

Remarque: Ceci est différent de cette question (à part la langue) car j'ajoute les deux entiers.

55
Thomas

Avis de non-responsabilité (comme suggéré par Toby Speight): Bien que les représentations IEEE 754 soient assez courantes, une implémentation est autorisée à utiliser toute autre représentation qui satisfait aux exigences du langage.


Les doubles sont représentés sous la forme mantissa * 2^exponent, c'est-à-dire que certains des bits sont utilisés pour la partie non entière du nombre double.

             bits        range                       precision
  float        32        1.5E-45   .. 3.4E38          7- 8 digits
  double       64        5.0E-324  .. 1.7E308        15-16 digits
  long double  80        1.9E-4951 .. 1.1E4932       19-20 digits

Schematic of IEEE 754 double type

La partie de la fraction peut également être utilisée pour représenter un entier en utilisant un exposant qui supprime tous les chiffres après le point.

Par exemple. 2.9979 · 10 ^ 4 = 29979.

Étant donné qu'un int commun est généralement de 32 bits, vous pouvez représenter tous les int comme des doubles, mais pour les entiers 64 bits, bien sûr, ce n'est plus le cas. Pour être plus précis (comme le note LThode dans un commentaire): la double précision IEEE 754 peut garantir cela jusqu'à 53 bits (52 bits de signification + le bit implicite de 1 bit).

Réponse : oui pour les entrées 32 bits, non pour les entrées 64 bits.

(Ceci est correct pour les environnements de processeur à usage général de serveur/bureau, mais d'autres architectures peuvent se comporter différemment.)

Réponse pratique comme le dit Malcom McLean: les doubles 64 bits sont un type d'entier adéquat pour presque tous les entiers susceptibles de compter des choses dans la vie réelle.


Pour les personnes empiriquement inclinées, essayez this :

#include <iostream>
#include <limits>
using namespace std;

int main() {
    double test;
    volatile int test_int;
    for(int i=0; i< std::numeric_limits<int>::max(); i++) {
        test = i;
        test_int = test;

        // compare int with int:
        if (test_int != i)
            std::cout<<"found integer i="<<i<<", test="<<test<<std::endl;
    }
    return 0;
}

Temps de réussite: 0,85 mémoire: 15240 signal: 0


Sous-question : Concernant la question des différences fractionnaires. Est-il possible d'avoir un entier qui se transforme en un double qui est juste à côté de la valeur correcte par une fraction, mais qui se reconvertit au même entier en raison de l'arrondi?

La réponse est non, car tout entier qui se transforme d'avant en arrière à la même valeur, représente en fait la même valeur entière en double. Pour moi, l'explication la plus simple (suggérée par ilkkachu) est que l'utilisation de l'exposant 2^exponent la largeur de pas doit toujours être une puissance de deux. Par conséquent, au-delà du plus grand entier de 52 bits (signe +1), il n'y a jamais deux valeurs doubles avec une distance inférieure à 2, ce qui résout le problème d'arrondi.

71
Beginner

Supposons que vous ayez un type entier 64 bits et un type à virgule flottante 64 bits (ce qui est typique pour un double). Il existe 2 ^ 64 valeurs possibles pour ce type entier et il y a 2 ^ 64 valeurs possibles pour ce type à virgule flottante. Mais certaines de ces valeurs à virgule flottante (en fait, la plupart d'entre elles) ne représentent pas des valeurs entières, donc le type à virgule flottante peut représenter moins de valeurs entières que le type entier.

15
Pete Becker

La réponse est non. Cela ne fonctionne que si ints est 32 bits, ce qui, bien que vrai sur la plupart des plates-formes, n'est pas garanti par la norme.

Les deux entiers peuvent partager la même représentation double.

Par exemple, this

#include <iostream>
int main() {
    int64_t n = 2397083434877565865;
    if (static_cast<double>(n) == static_cast<double>(n - 1)) {
        std::cout << "n and (n-1) share the same double representation\n";
    }
}    

imprimera

n et (n-1) partagent la même représentation double

C'est à dire. les deux 2397083434877565865 et 2397083434877565864 seront convertis en le même double.

Notez que j'ai utilisé int64_t ici pour garantir des entiers 64 bits, qui - selon votre plate-forme - pourraient également être ce que int est.

11
Corristo

Vous avez 2 questions différentes:

Toutes les valeurs entières sont-elles parfaitement représentées comme des doubles?

Cela a déjà été répondu par d'autres personnes (TL; DR: cela dépend de la précision de int et double).

Considérez l'exemple de code suivant qui affiche "Identique": [...] Est-ce garanti pour toute architecture, tout compilateur, toute valeur de a et b?

Votre code ajoute deux ints et puis convertit le résultat en double. La somme de ints débordera pour certaines valeurs, mais la somme des deux doubles convertis séparément ne sera pas (typiquement). Pour ces valeurs, les résultats seront différents.

4
Pablo H

La réponse courte est "peut-être". La réponse portable est "pas partout".

Cela dépend vraiment de votre plateforme, et en particulier de

  • la taille et la représentation de double
  • la plage de int

Pour les plates-formes utilisant IEEE-754 doubles, cela peut être vrai si int est de 53 bits ou plus petit. Pour les plateformes où int est plus grande que double, c'est évidemment faux.

Vous souhaiterez peut-être étudier les propriétés de votre hôte d'exécution à l'aide de std::numeric_limits et std::nextafter.

2
Toby Speight