c++ - round - float precision




Quelle est la difference entre float et double? (8)

J'ai lu à propos de la différence entre la double précision et la simple précision. Cependant, dans la plupart des cas, le float et le double semblent être 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?


Énorme différence.

Comme son nom l'indique, un double a 2x la précision de float [1] . En général, un double a 15 chiffres décimaux de précision, tandis que le float a 7.

Voici comment le nombre de chiffres est calculé:

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

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

Cette perte de précision pourrait conduire à des erreurs de troncature beaucoup plus faciles à faire flotter, 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

En outre, la valeur maximale de float est d'environ 3e38 , mais le double est d'environ 1.7e308 , donc l'utilisation de float peut atteindre "infinity" (un nombre à virgule flottante) beaucoup plus facilement que double pour quelque chose de simple, par exemple .

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

Bien sûr, parfois, même le double n'est pas assez précis, donc nous avons 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, le traitement de l'argent), vous devez utiliser int ou une classe de fraction.

De plus, n'utilisez pas += 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 standards C et C ++ ne spécifient pas la représentation de float , double et long double . Il est possible que tous les trois sont implémentés en tant que 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 (binary32), et double est un nombre à virgule flottante double précision IEEE (binary64).


É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 float et 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 grands, mais vous obtenez toujours des effets d'annulation en utilisant float .

(En fait, ce qui précède n'est pas la meilleure façon 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 on utilise une méthode plus stable .)


J'ai juste couru dans une erreur qui m'a pris pour toujours pour comprendre et peut potentiellement vous donner un bon exemple de précision de flotteur.

#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 mets en place le 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.


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

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

Les tests peuvent spécifiquement utiliser des numéros qui provoqueraient ce genre d'erreur et donc testé que vous avez utilisé le type approprié dans votre code.


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


Lorsque vous utilisez des nombres à virgule flottante, vous ne pouvez pas croire que vos tests locaux seront exactement les mêmes que ceux qui sont effectués sur le 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 déjà vu ce problème plusieurs fois dans certaines compétitions TopCoder, surtout si vous essayez de comparer deux nombres à virgule flottante.


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 , double et long double . Le type double fournit au moins autant de précision que float , et le type long double fournit au moins autant de précision que le double . L'ensemble des valeurs du type float est un sous-ensemble de l'ensemble des valeurs du type double ; l'ensemble des valeurs du type double est un sous-ensemble de l'ensemble des valeurs du type long double .

La norme C ++ ajoute:

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

Je suggère de jeter un coup d'oeil à l'excellent Ce que tout scientifique informatique devrait savoir sur l'arithmétique à virgule flottante qui couvre en profondeur la norme à virgule flottante de l'IEEE. Vous découvrirez les détails de la représentation et vous réaliserez qu'il y a un compromis entre l'ampleur et la précision. La précision de la représentation en virgule flottante augmente au fur et à mesure que l'amplitude décroît, ainsi les nombres à virgule flottante compris entre -1 et 1 sont ceux qui ont le plus de précision.


  • Un double est de 64 et la précision simple (float) est de 32 bits.
  • Le double a une mantisse plus grande (les bits entiers du nombre réel).
  • Toutes les inexactitudes seront plus petites dans le double.






precision