it-swarm.dev

Was ist der Unterschied zwischen Float und Double?

Ich habe über den Unterschied zwischen doppelter Genauigkeit und einfacher Genauigkeit gelesen. In den meisten Fällen scheinen jedoch float und double austauschbar zu sein, d. H. Die Verwendung des einen oder des anderen scheint die Ergebnisse nicht zu beeinflussen. Ist das wirklich so? Wann sind Floats und Doubles austauschbar? Was sind die Unterschiede zwischen ihnen?

351
VaioIsBorn

Riesiger Unterschied.

Wie der Name schon sagt, hat eine double die doppelte Genauigkeit von float[1]. Im Allgemeinen hat eine double eine Genauigkeit von 15 Dezimalstellen, während float 7 hat.

So wird die Anzahl der Ziffern berechnet:

double hat 52 Mantissenbits + 1 verstecktes Bit: log (253) ÷ Protokoll (10) = 15,95 Stellen

float hat 23 Mantissenbits + 1 verstecktes Bit: log (224) ÷ log (10) = 7,22 Ziffern

Dieser Präzisionsverlust könnte zu Abschneidefehlern führen, die viel einfacher zu schweben sind, z.

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

Der maximale Wert von float ist ungefähr 3e38, aber double ist 1.7e308, so dass die Verwendung von float "unendlich" (d. H. Eine spezielle Gleitkommazahl) viel einfacher als double für etwas einfaches, z. Berechnung der Fakultät von 60.

Während des Tests enthalten einige Testfälle möglicherweise diese großen Zahlen, was dazu führen kann, dass Ihre Programme bei Verwendung von Floats fehlschlagen.


Natürlich ist manchmal sogar double nicht genau genug, daher haben wir manchmal long double[1] (Das obige Beispiel ergibt 9.00000000000000000066 auf dem Mac), aber alle Gleitkommatypen leiden an round-off-Fehlern. Wenn also Präzision sehr wichtig ist (z. B. Geldverarbeitung), sollten Sie int oder eine Bruchklasse verwenden.


Verwenden Sie nicht +=, um viele Gleitkommazahlen zu summieren, da sich die Fehler schnell ansammeln. Wenn Sie Python verwenden, verwenden Sie fsum. Andernfalls versuchen Sie den Kahan-Summationsalgorithmus zu implementieren.


[1]: Die C- und C++ - Standards spezifizieren nicht die Darstellung von float, double und long double. Es ist möglich, dass alle drei als IEEE-Double-Precision implementiert sind. Für die meisten Architekturen (gcc, MSVC; x86, x64, ARM) ist floattatsächlich eine Gleitkommazahl nach IEEE mit einfacher Genauigkeit (binary32) und double eine Gleitkommazahl nach IEEE mit doppelter Genauigkeit (binary64).

434
kennytm

Folgendes sagen die Standards 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 und long double. Der Typ double bietet mindestens die gleiche Genauigkeit wie float und der Typ long double bietet mindestens die gleiche Genauigkeit wie double. Die Menge von Werten des Typs float ist eine Teilmenge der Menge von Werten des Typs double; Die Menge von Werten des Typs double ist eine Teilmenge der Menge von Werten des Typs long double.

Der C++ - Standard fügt hinzu:

Die Wertdarstellung von Gleitkommatypen ist implementierungsdefiniert.

Ich würde vorschlagen, einen Blick auf das hervorragende Was jeder Informatiker über Gleitkomma-Arithmetik wissen sollte, das den IEEE-Gleitkommastandard ausführlich behandelt. Sie lernen die Details der Repräsentation kennen und erkennen, dass es einen Kompromiss zwischen Größe und Präzision gibt. Die Genauigkeit der Fließkommadarstellung steigt mit abnehmender Größe. Daher sind Fließkommazahlen zwischen -1 und 1 die mit der höchsten Genauigkeit.

51
Gregory Pakosz

Gegeben eine quadratische Gleichung: x2- 4.0000000 x + 3.9999999 = 0, die genauen Wurzeln auf 10 signifikante Stellen sind r1= 2.000316228 und r2= 1.999683772.

Mit float und 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;
}  

Das Programm auszuführen gibt mir:

2.00000 2.00000
2.00032 1.99968

Beachten Sie, dass die Zahlen nicht groß sind, dass Sie jedoch mit float Annullierungseffekte erhalten.

(Tatsächlich ist das Obige nicht der beste Weg, um quadratische Gleichungen mit Gleitkommazahlen mit einfacher oder doppelter Genauigkeit zu lösen, aber die Antwort bleibt unverändert, auch wenn eine stabilere Methode verwendet wird.)

26
Alok Singhal
  • Ein Double ist 64 und eine einfache Genauigkeit .__ (Float) ist 32 Bit.
  • Das Double hat eine größere Mantisse (die ganzzahligen Bits der reellen Zahl).
  • Alle Ungenauigkeiten werden im Doppelten kleiner.
19
graham.reeds

Die Größe der an den Float-Point-Berechnungen beteiligten Zahlen ist nicht das Wichtigste. Es ist die Berechnung, die durchgeführt wird, die relevant ist. 

Wenn Sie eine Berechnung durchführen und das Ergebnis eine irrationale Zahl oder eine wiederkehrende Dezimalzahl ist, kommt es im Wesentlichen zu Rundungsfehlern, wenn diese Zahl in der von Ihnen verwendeten Datenstruktur mit endlicher Größe komprimiert wird. Da double doppelt so groß ist wie der Float, ist der Rundungsfehler viel kleiner.

Bei den Tests werden möglicherweise Zahlen verwendet, die diese Art von Fehler verursachen und daher getestet haben, dass Sie den entsprechenden Typ in Ihrem Code verwendet haben.

11
Dolbz

Der Typ Float, 32 Bit lang, hat eine Genauigkeit von 7 Stellen. Zwar können Werte mit sehr großem oder sehr kleinem Bereich (+/- 3,4 * 10 ^ 38 oder * 10 ^ -38) gespeichert werden, es hat jedoch nur 7 signifikante Stellen.

Typ double, 64 Bit lang, hat einen größeren Bereich (* 10 ^ +/- 308) und eine Genauigkeit von 15 Stellen.

Der Typ long double ist nominell 80 Bit, obwohl eine bestimmte Compiler/OS-Kopplung es für Ausrichtungszwecke als 12-16 Byte speichern kann. Das lange Doppel hat einen Exponenten, der einfach lächerlich groß ist und sollte eine Genauigkeit von 19 Stellen haben. Microsoft begrenzt in seiner unendlichen Weisheit long double auf 8 Byte, genau wie einfaches double.

Verwenden Sie im Allgemeinen einfach type double, wenn Sie einen Gleitkommawert/eine Variable benötigen. In Ausdrücken verwendete wörtliche Gleitkommawerte werden standardmäßig als Verdopplungen behandelt, und die meisten mathematischen Funktionen, die Gleitkommawerte zurückgeben, geben Verdopplungen zurück. Sie sparen sich viele Kopfschmerzen und Typografien, wenn Sie einfach double verwenden.

8
Zain Ali

Schwimmer haben eine geringere Genauigkeit als Doppelte. Obwohl Sie bereits wissen, lesen Sie Was WE über Gleitkomma-Arithmetik wissen sollte zum besseren Verständnis.

8
N 1.1

Ich bin gerade auf einen Fehler gestoßen, der mich immer wieder geklärt hat und möglicherweise ein gutes Beispiel für die Genauigkeit des Schwimmers liefern kann.

#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 ist

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 nach 0,83 sehen können, sinkt die Präzision erheblich.

Wenn ich jedoch t als doppelt einrichten, tritt ein solches Problem nicht auf.

Ich habe fünf Stunden gebraucht, um diesen kleinen Fehler zu erkennen, der mein Programm ruiniert hat.

7
Elliscope Fang

Bei der Verwendung von Gleitkommazahlen können Sie nicht darauf vertrauen, dass Ihre lokalen Tests genau den Tests auf der Serverseite entsprechen. Die Umgebung und der Compiler unterscheiden sich wahrscheinlich auf Ihrem lokalen System und wo die abschließenden Tests ausgeführt werden. Ich habe dieses Problem schon oft in einigen TopCoder-Wettbewerben gesehen, insbesondere wenn Sie versuchen, zwei Gleitkommazahlen zu vergleichen.

3
Tuomas Pelkonen

Die integrierten Vergleichsoperationen unterscheiden sich dahingehend, dass beim Vergleich von 2 Zahlen mit Fließkomma der Unterschied im Datentyp (d. H. Float oder Double) zu unterschiedlichen Ergebnissen führt.

2
Johnathan Lau

Der Unterschied zwischen float und double besteht darin, dass double einen höheren Genauigkeitswert hat als float-Variablen . Wenn Sie eine Variable als float deklarieren, können nur 6 Dezimalstellen nach dem Dezimalpunkt eingegeben werden ..__ Eine Float-Variable float f= 2.3333333; // 7 Dezimalstellen hinter dem . ist das Maximum, das Sie speichern können

Selbst wenn Sie einen Wert speichern, der mehr als sechs Stellen hinter dem Dezimalzeichen liegt, wird die ganze Zahl nicht gespeichert, sondern nur bis zu den ersten sechs Stellen nach dem Dezimalpunkt . Außerdem erhalten Sie einen Fehler wenn Sie versuchen, mehr als 7 Stellen nach dem Dezimalzeichen für diese Variable zu speichern. In diesem Fall müssen Sie es wie folgt initialisieren: -float f= 2.3333334443f; // dann wird dies vom Compiler als 2.3333334 interpretiert

Im Falle von Double speichert es bis zu 15 Stellen nach dem Dezimalpunkt . Beispiel: double d=1.222222345675423; // 15 Ziffern nach dem Dezimalpunkt

0