Quelle est la différence entre float et double?


420

J'ai lu la différence entre la double précision et la simple précision. Cependant, dans la plupart des cas, floatet doublesemblent interchangeables, c'est-à-dire que l'utilisation de l'un ou de l'autre ne semble pas affecter les résultats. Est-ce vraiment le cas? Quand les flotteurs et les doubles sont-ils interchangeables? Quelles sont les différences entre eux?

Réponses:


521

Énorme différence.

Comme son nom l'indique, a doublea 2x la précision de [1] . En général, a a 15 chiffres décimaux de précision, alors qu'il en a 7.floatdoublefloat

Voici comment le nombre de chiffres est calculé:

doublea 52 bits de mantisse + 1 bit caché: log (2 53 ) ÷ log (10) = 15,95 chiffres

floata 23 bits de mantisse + 1 bit caché: log (2 24 ) ÷ log (10) = 7,22 chiffres

Cette perte de précision pourrait entraîner une accumulation d'erreurs de troncature plus importantes lorsque des calculs répétés sont effectués, par exemple

float a = 1.f / 81;
float b = 0;
for (int i = 0; i < 729; ++ i)
    b += a;
printf("%.7g\n", b); // prints 9.000023

tandis que

double a = 1.0 / 81;
double b = 0;
for (int i = 0; i < 729; ++ i)
    b += a;
printf("%.15g\n", b); // prints 8.99999999999996

De plus, la valeur maximale de float est d'environ 3e38, mais le double est d'environ 1.7e308, donc l'utilisation floatpeut frapper "l'infini" (c'est-à-dire un nombre spécial à virgule flottante) beaucoup plus facilement que doublepour quelque chose de simple, par exemple le calcul de la factorielle de 60.

Pendant les tests, quelques cas de test contiennent peut-être ces énormes nombres, ce qui peut entraîner l'échec de vos programmes si vous utilisez des flottants.


Bien sûr, parfois, même doublepas assez précis, nous avons donc parfois long double[1] (l'exemple ci-dessus donne 9.000000000000000066 sur Mac), mais tous les types à virgule flottante souffrent d' erreurs d' arrondi , donc si la précision est très importante (par exemple, l'argent vous devez utiliser intou une classe de fraction.


De plus, ne pas utiliser +=pour additionner beaucoup de nombres à virgule flottante, car les erreurs s'accumulent rapidement. Si vous utilisez Python, utilisez fsum. Sinon, essayez d'implémenter l' algorithme de sommation de Kahan .


[1]: Les normes C et C ++ ne spécifient pas la représentation de float, doubleet long double. Il est possible que les trois soient implémentés en double précision IEEE. Néanmoins, pour la plupart des architectures (gcc, MSVC; x86, x64, ARM) float est en effet un nombre à virgule flottante simple précision IEEE (binaire32), et double est un nombre à virgule flottante double précision IEEE (binaire64).


9
Le conseil habituel pour la sommation est de trier vos nombres à virgule flottante par ordre de grandeur (le plus petit en premier) avant la sommation.
R .. GitHub STOP HELPING ICE

Notez que tandis que C / C ++ float et double sont presque toujours simples et double précision IEEE respectivement C / C ++ long double est beaucoup plus variable en fonction de votre CPU, compilateur et OS. Parfois, c'est la même chose que le double, parfois c'est un format étendu spécifique au système, Parfois c'est la précision IEEE quad.
plugwash

@ R..GitHubSTOPHELPINGICE: pourquoi? Pourriez-vous expliquer?
InQusitive

@InQusitive: Considérons par exemple un tableau composé de la valeur 2 ^ 24 suivie de 2 ^ 24 répétitions de la valeur 1. La sommation dans l'ordre produit 2 ^ 24. L'inversion produit 2 ^ 25. Bien sûr, vous pouvez faire des exemples (par exemple, faire 2 ^ 25 répétitions de 1) où n'importe quel ordre finit par être catastrophiquement mauvais avec un seul accumulateur, mais la plus petite ampleur en premier est la meilleure parmi celles-ci. Pour faire mieux, vous avez besoin d'une sorte d'arbre.
R .. GitHub ARRÊTEZ D'AIDER LA GLACE

56

Voici ce que disent les normes C99 (ISO-IEC 9899 6.2.5 §10) ou C ++ 2003 (ISO-IEC 14882-2003 3.1.9 §8):

Il existe trois types de virgule flottante: float, doubleet long double. Le type doublefournit au moins autant de précision que float, et le type long doublefournit au moins autant de précision que double. L'ensemble de valeurs du type floatest un sous-ensemble de l'ensemble de valeurs du type double; l'ensemble de valeurs du type doubleest un sous-ensemble de l'ensemble de valeurs du type long double.

La norme C ++ ajoute:

La représentation des valeurs des types à virgule flottante est définie par l'implémentation.

Je suggérerais de jeter un coup d'œil à l'excellent ce que tout informaticien devrait savoir sur l'arithmétique à virgule flottante qui couvre en profondeur la norme à virgule flottante IEEE. Vous en apprendrez plus sur les détails de la représentation et vous vous rendrez compte qu'il existe un compromis entre la magnitude et la précision. La précision de la représentation en virgule flottante augmente à mesure que l'amplitude diminue, donc les nombres en virgule flottante entre -1 et 1 sont ceux qui ont le plus de précision.


27

Étant donné une équation quadratique: x 2  - 4.0000000  x  + 3.9999999 = 0, les racines exactes à 10 chiffres significatifs sont, r 1  = 2.000316228 et r 2  = 1.999683772.

En utilisant floatet double, nous pouvons écrire un programme de test:

#include <stdio.h>
#include <math.h>

void dbl_solve(double a, double b, double c)
{
    double d = b*b - 4.0*a*c;
    double sd = sqrt(d);
    double r1 = (-b + sd) / (2.0*a);
    double r2 = (-b - sd) / (2.0*a);
    printf("%.5f\t%.5f\n", r1, r2);
}

void flt_solve(float a, float b, float c)
{
    float d = b*b - 4.0f*a*c;
    float sd = sqrtf(d);
    float r1 = (-b + sd) / (2.0f*a);
    float r2 = (-b - sd) / (2.0f*a);
    printf("%.5f\t%.5f\n", r1, r2);
}   

int main(void)
{
    float fa = 1.0f;
    float fb = -4.0000000f;
    float fc = 3.9999999f;
    double da = 1.0;
    double db = -4.0000000;
    double dc = 3.9999999;
    flt_solve(fa, fb, fc);
    dbl_solve(da, db, dc);
    return 0;
}  

L'exécution du programme me donne:

2.00000 2.00000
2.00032 1.99968

Notez que les nombres ne sont pas importants, mais vous obtenez toujours des effets d'annulation en utilisant float.

(En fait, ce qui précède n'est pas le meilleur moyen de résoudre des équations quadratiques en utilisant des nombres à virgule flottante simple ou double précision, mais la réponse reste inchangée même si l'on utilise une méthode plus stable .)


19
  • Un double vaut 64 et une simple précision (float) est 32 bits.
  • Le double a une mantisse plus grande (les bits entiers du nombre réel).
  • Toute inexactitude sera plus petite dans le double.

12

La taille des nombres impliqués dans les calculs en virgule flottante n'est pas la chose la plus pertinente. C'est le calcul qui est effectué qui est pertinent.

En gros, si vous effectuez un calcul et que le résultat est un nombre irrationnel ou décimal récurrent, il y aura des erreurs d'arrondi lorsque ce nombre sera écrasé dans la structure de données de taille finie que vous utilisez. Étant donné que le double est deux fois la taille du flottant, l'erreur d'arrondi sera beaucoup plus petite.

Les tests peuvent spécifiquement utiliser des nombres qui provoqueraient ce type d'erreur et ont donc testé que vous aviez utilisé le type approprié dans votre code.


9

Le type float, long de 32 bits, a une précision de 7 chiffres. Bien qu'il puisse stocker des valeurs avec une plage très grande ou très petite (+/- 3,4 * 10 ^ 38 ou * 10 ^ -38), il n'a que 7 chiffres significatifs.

Le type double, 64 bits de long, a une plus grande plage (* 10 ^ + / - 308) et une précision de 15 chiffres.

Le type long double est nominalement de 80 bits, bien qu'un appariement compilateur / OS donné puisse le stocker en tant que 12-16 octets à des fins d'alignement. Le long double a un exposant qui est ridiculement énorme et devrait avoir une précision de 19 chiffres. Microsoft, dans sa sagesse infinie, limite le double long à 8 octets, le même que le double simple.

De manière générale, utilisez simplement type double lorsque vous avez besoin d'une valeur / variable à virgule flottante. Les valeurs littérales en virgule flottante utilisées dans les expressions seront traitées comme des doubles par défaut, et la plupart des fonctions mathématiques qui renvoient des valeurs en virgule flottante retournent des doubles. Vous vous épargnerez de nombreux maux de tête et transtypages si vous utilisez simplement le double.



9

Je viens de rencontrer une erreur qui m'a pris une éternité à comprendre et peut potentiellement vous donner un bon exemple de précision de flottement.

#include <iostream>
#include <iomanip>

int main(){
  for(float t=0;t<1;t+=0.01){
     std::cout << std::fixed << std::setprecision(6) << t << std::endl;
  }
}

La sortie est

0.000000
0.010000
0.020000
0.030000
0.040000
0.050000
0.060000
0.070000
0.080000
0.090000
0.100000
0.110000
0.120000
0.130000
0.140000
0.150000
0.160000
0.170000
0.180000
0.190000
0.200000
0.210000
0.220000
0.230000
0.240000
0.250000
0.260000
0.270000
0.280000
0.290000
0.300000
0.310000
0.320000
0.330000
0.340000
0.350000
0.360000
0.370000
0.380000
0.390000
0.400000
0.410000
0.420000
0.430000
0.440000
0.450000
0.460000
0.470000
0.480000
0.490000
0.500000
0.510000
0.520000
0.530000
0.540000
0.550000
0.560000
0.570000
0.580000
0.590000
0.600000
0.610000
0.620000
0.630000
0.640000
0.650000
0.660000
0.670000
0.680000
0.690000
0.700000
0.710000
0.720000
0.730000
0.740000
0.750000
0.760000
0.770000
0.780000
0.790000
0.800000
0.810000
0.820000
0.830000
0.839999
0.849999
0.859999
0.869999
0.879999
0.889999
0.899999
0.909999
0.919999
0.929999
0.939999
0.949999
0.959999
0.969999
0.979999
0.989999
0.999999

Comme vous pouvez le voir après 0,83, la précision diminue considérablement.

Cependant, si je configure en ttant que double, un tel problème ne se produira pas.

Il m'a fallu cinq heures pour réaliser cette erreur mineure, qui a ruiné mon programme.


4
juste pour être sûr: la solution de votre problème devrait être d'utiliser un int de préférence? Si vous voulez répéter 100 fois, vous devriez compter avec un int plutôt qu'avec un double
BlueTrin

8
L'utilisation doublen'est pas une bonne solution ici. Vous utilisez intpour compter et faire une multiplication interne pour obtenir votre valeur à virgule flottante.
Richard


3

Lorsque vous utilisez des nombres à virgule flottante, vous ne pouvez pas être sûr que vos tests locaux seront exactement les mêmes que ceux effectués côté serveur. L'environnement et le compilateur sont probablement différents sur votre système local et où les tests finaux sont exécutés. J'ai vu ce problème plusieurs fois auparavant dans certaines compétitions TopCoder, surtout si vous essayez de comparer deux nombres à virgule flottante.


3

Les opérations de comparaison intégrées diffèrent comme lorsque vous comparez 2 nombres à virgule flottante, la différence de type de données (c'est-à-dire flottant ou double) peut entraîner des résultats différents.


1

Si l'on travaille avec un traitement intégré, le matériel sous-jacent (par exemple, FPGA ou un modèle de processeur / microcontrôleur spécifique) sera implémenté de manière optimale dans le matériel, tandis que le double utilisera des routines logicielles. Donc, si la précision d'un float est suffisante pour gérer les besoins, le programme s'exécutera quelques fois plus rapidement avec float puis doublera. Comme indiqué dans d'autres réponses, méfiez-vous des erreurs d'accumulation.


-1

Contrairement à un int(nombre entier), a floata un point décimal, tout comme a double. Mais la différence entre les deux est que a doubleest deux fois plus détaillé que a float, ce qui signifie qu'il peut avoir le double du nombre de chiffres après la virgule décimale.


4
Cela ne signifie pas du tout cela. Cela signifie en fait deux fois plus de intégrantes chiffres décimaux, et il est plus que double. La relation entre les chiffres fractionnaires et la précision n'est pas linéaire: elle dépend de la valeur: par exemple 0,5 est précis mais 0,33333333333333333333 ne l'est pas.
Marquis de Lorne
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.