Ich habe über den Unterschied zwischen doppelter Präzision und einfacher Präzision gelesen. Allerdings in den meisten Fällen, float
y double
scheinen austauschbar zu sein, d. h. die Verwendung des einen oder des anderen scheint die Ergebnisse nicht zu beeinflussen. Ist dies wirklich der Fall? Wann sind Floats und Doubles austauschbar? Was sind die Unterschiede zwischen ihnen?
Antworten
Zu viele Anzeigen?Ein gewaltiger Unterschied.
Wie der Name schon sagt, ist ein double
hat die doppelte Genauigkeit von float
[1] . Im Allgemeinen ist eine double
hat eine Genauigkeit von 15 Dezimalstellen, während float
hat 7.
So wird die Anzahl der Ziffern berechnet:
double
hat 52 Mantissenbits + 1 verstecktes Bit: log(2 53 )÷log(10) = 15,95 Ziffern
float
hat 23 Mantissenbits + 1 verstecktes Bit: log(2 24 )÷log(10) = 7,22 Stellen
Dieser Präzisionsverlust könnte dazu führen, dass sich bei wiederholten Berechnungen größere Abbruchfehler ansammeln, z. B.
float a = 1.f / 81;
float b = 0;
for (int i = 0; i < 729; ++ i)
b += a;
printf("%.7g\n", b); // prints 9.000023
während
double a = 1.0 / 81;
double b = 0;
for (int i = 0; i < 729; ++ i)
b += a;
printf("%.15g\n", b); // prints 8.99999999999996
Außerdem liegt der Höchstwert von float bei etwa 3e38
aber das Doppelte ist etwa 1.7e308
also mit float
kann "Unendlich" (d. h. eine spezielle Gleitkommazahl) viel leichter erreichen als double
für etwas Einfaches, z. B. die Berechnung der Fakultät von 60.
Während des Testens enthalten vielleicht einige Testfälle diese riesigen Zahlen, was dazu führen kann, dass Ihre Programme fehlschlagen, wenn Sie Fließkommazahlen verwenden.
Natürlich, manchmal sogar double
ist nicht genau genug, daher müssen wir manchmal long double
[1] (das obige Beispiel ergibt 9.000000000000000066 auf Mac), aber alle Gleitkommatypen leiden unter Rundungsfehler Wenn also Präzision sehr wichtig ist (z. B. bei der Geldbearbeitung), sollten Sie int
oder eine Fraktionsklasse.
Verwenden Sie außerdem nicht +=
um viele Fließkommazahlen zu summieren, da sich die Fehler schnell summieren. Wenn Sie Python verwenden, benutzen Sie fsum
. Andernfalls sollten Sie versuchen, die Kahan-Summationsalgorithmus .
[1]: Die C- und C++-Standards spezifizieren nicht die Darstellung von float
, double
y long double
. Es ist möglich, dass alle drei als IEEE double-precision implementiert sind. Dennoch gilt für die meisten Architekturen (gcc, MSVC; x86, x64, ARM) float
est in der Tat eine IEEE-Gleitkommazahl mit einfacher Genauigkeit (binary32), und double
est eine IEEE-Gleitkommazahl mit doppelter Genauigkeit (binary64).
So steht es in den Normen C99 (ISO-IEC 9899 6.2.5 §10) oder C++2003 (ISO-IEC 14882-2003 3.1.9 §8):
Es gibt drei Gleitkommatypen:
float
,double
ylong double
. Der Typdouble
bietet mindestens so viel Präzision wiefloat
und der Typlong double
bietet mindestens so viel Präzision wiedouble
. Die Menge der Werte des Typsfloat
ist eine Teilmenge der Menge der Werte des Typsdouble
die Menge der Werte des Typsdouble
ist eine Teilmenge der Menge der Werte des Typslong double
.
Der C++-Standard fügt hinzu:
Die Wertedarstellung von Gleitkommatypen ist implementierungsabhängig.
Ich würde vorschlagen, einen Blick auf die ausgezeichnete Was jeder Informatiker über Fließkommaarithmetik wissen sollte in dem der IEEE-Gleitkommastandard ausführlich behandelt wird. Sie lernen die Details der Darstellung kennen und werden feststellen, dass es einen Kompromiss zwischen Größe und Genauigkeit gibt. Die Genauigkeit der Fließkommadarstellung nimmt mit abnehmendem Betrag zu, daher sind Fließkommazahlen zwischen -1 und 1 diejenigen mit der höchsten Genauigkeit.
Gegeben eine quadratische Gleichung: x 2 - 4.0000000 x + 3,9999999 = 0, die genauen Wurzeln auf 10 signifikante Stellen genau sind, r 1 = 2,000316228 und r 2 = 1.999683772.
Verwendung von float
y double
können wir ein Testprogramm schreiben:
#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;
}
Wenn ich das Programm ausführe, erhalte ich:
2.00000 2.00000
2.00032 1.99968
Beachten Sie, dass die Zahlen nicht groß sind, aber Sie erhalten dennoch Aufhebungseffekte, wenn Sie float
.
(Tatsächlich ist die obige Methode nicht die beste, um quadratische Gleichungen mit einfach- oder doppeltgenauen Gleitkommazahlen zu lösen, aber die Antwort bleibt unverändert, auch wenn man eine stabilere Methode .)
Ich bin gerade auf einen Fehler gestoßen, für den ich ewig brauchte, um ihn zu lösen, und der Ihnen möglicherweise ein gutes Beispiel für die Genauigkeit von Schwebekörpern liefert.
#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;
}
}
Die Ausgabe lautet
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
Wie Sie sehen können, nimmt die Genauigkeit nach 0,83 deutlich ab.
Wenn ich jedoch die Option t
als Doppelgänger, wird ein solches Problem nicht auftreten.
Ich habe fünf Stunden gebraucht, um diesen kleinen Fehler zu erkennen, der mein Programm ruiniert hat.
- See previous answers
- Weitere Antworten anzeigen