Wat is het verschil tussen float en dubbele?

Ik heb gelezen over het verschil tussen dubbele precisie en enkele precisie. In de meeste gevallen lijkt floaten doubleWisselbaar te zijn, d.w.z. het gebruik van een of de ander niet lijkt te beïnvloeden van de resultaten. Is dit echt het geval? Wanneer zijn drijvers en verdubbeling verwisselbaar? Wat zijn de verschillen tussen hen?


Antwoord 1, Autoriteit 100%

Enorm verschil.

Zoals de naam impliceert, een doubleheeft 2x De precisie van float[1] . In het algemeen een doubleheeft 15 decimale cijfers van precisie, terwijl floatheeft 7.

Hier is hoe het aantal cijfers wordt berekend:

doubleheeft 52 mantissa bits + 1 verborgen bit: log (2 53 ) ÷ log (10) = 15.95 cijfers

floatheeft 23 mantissa bits + 1 verborgen bit: log (2 24 ) ÷ Log (10) = 7,22 cijfers

Dit precisieverlies kan leiden tot grotere truncatiefouten die worden geaccumuleerd wanneer herhaalde berekeningen worden uitgevoerd, b.v.

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

terwijl

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

Ook is de maximale waarde van float ongeveer 3e38, maar dubbel gaat over 1.7e308, dus met floatkan “Infinity” raken (dwz een speciaal drijvend nummer) veel gemakkelijker dan doublevoor iets eenvoudigs, bijv het berekenen van het factoriaal van 60.

Tijdens het testen bevatten misschien een paar testcases deze enorme cijfers, waardoor uw programma’s mislukt als u drijvers gebruikt.


Natuurlijk, soms, zelfs doubleis niet nauwkeurig genoeg, vandaar hebben we soms long double[1] (het bovenstaande voorbeeld Geeft 9.000000000000000066 op Mac), maar alle drijvende puntentypen lijden aan afrondingsfouten , dus als precisie erg belangrijk is (bijv. Geldverwerking), moet u intof een fractie gebruiken klasse.


Gebruik bovendien niet +=om veel zwevende puntnummers samen te stellen, aangezien de fouten snel accumuleren. Als u Python gebruikt, gebruikt u fsum. Anders probeer het Kahan SumPation Algorithm te verkennen.


[1]: De C- en C++ -normen Geef de weergave van float, doubleen long double. Het is mogelijk dat alle drie worden geïmplementeerd als IEEE Double-Precision. Desalniettemin, voor de meeste architecturen (GCC, MSVC; X86, X64, ARM) floatis inderdaad een IEEE-precisie drijvend puntnummer (Binary32) en doubleis een IEEE dubbel-precisie drijvend puntnummer (Binary64).


Antwoord 2, Autoriteit 10%

Hier is wat de standaard C95 (ISO-IEC 9899 6.2.5 §10) of C++ 2003 (ISO-IEC 14882-2003 3.1.9 §8) Normen zeggen:

Er zijn drie typen drijvende komma: float, doubleen long double. Het type doublegeeft minstens zoveel precisie als float, en het type long doublegeeft minstens zoveel precisie als double. De set waarden van het type floatis een subset van de set waarden van het type double; de set waarden van het type doubleis een subset van de set waarden van het type long double.

De C++-standaard voegt toe:

De waarderepresentatie van typen met drijvende komma is door de implementatie gedefinieerd.

Ik raad je aan om de uitstekende What Every Computerwetenschappers moeten weten over drijvende-komma-rekenkundedie de IEEE-standaard voor drijvende komma diepgaand behandelt. Je leert over de weergavedetails en je zult je realiseren dat er een afweging is tussen grootte en precisie. De precisie van de drijvende-kommaweergave neemt toe naarmate de grootte afneemt, dus drijvende-kommagetallen tussen -1 en 1 zijn die met de meeste precisie.


Antwoord 3, autoriteit 5%

Gegeven een kwadratische vergelijking: x2 − 4000000 x + 3.9999999 =  ;0, de exacte wortels tot 10 significante cijfers zijn, r1 = 2.000316228 en r2 = 1.999683772.

Met floaten doublekunnen we een testprogramma schrijven:

#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;
}  

Het uitvoeren van het programma geeft me:

2.00000 2.00000
2.00032 1.99968

Merk op dat de aantallen niet groot zijn, maar toch krijg je annuleringseffecten met float.

(In feite is het bovenstaande niet de beste manier om kwadratische vergelijkingen op te lossen met behulp van drijvende-kommagetallen met enkele of dubbele precisie, maar het antwoord blijft ongewijzigd, zelfs als men een stabielere methode.)


Antwoord 4, autoriteit 3%

  • Een dubbele is 64 en enkele precisie
    (float) is 32 bits.
  • De dubbele heeft een grotere mantisse (de gehele bits van het reële getal).
  • Onnauwkeurigheden worden kleiner in het dubbele.

Antwoord 5, autoriteit 2%

De grootte van de getallen die betrokken zijn bij de float-point berekeningen is niet het meest relevante. Het is de berekening die wordt uitgevoerd die relevant is.

In essentie, als u een berekening uitvoert en het resultaat is een irrationeel getal of een recurrente decimaal, dan zijn er afrondingsfouten wanneer dat aantal wordt ingepakt in de finite-grootte-gegevensstructuur die u gebruikt. Omdat Double tweemaal zo groot is van float, is de afrondingsfout een stuk kleiner.

De tests kunnen specifiek nummers gebruiken die dit soort fout veroorzaken en daarom wordt getest dat u het juiste type in uw code hebt gebruikt.


Antwoord 6, Autoriteit 2%

Ik heb net een fout opgetreden die me voor altijd kostte om erachter te komen en kan je potentieel een goed voorbeeld geven van floatprecisie.

#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;
  }
}

De uitvoer is

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

Zoals je kunt zien na 0,83, neemt de precisie aanzienlijk af.

Als ik techter als dubbel instel, zal een dergelijk probleem zich niet voordoen.

Het kostte me vijf uur om deze kleine fout te realiseren, die mijn programma verpestte.


Antwoord 7, autoriteit 2%

Type float, 32 bits lang, heeft een nauwkeurigheid van 7 cijfers. Hoewel het waarden kan opslaan met een zeer groot of zeer klein bereik (+/- 3,4 * 10^38 of * 10^-38), heeft het slechts 7 significante cijfers.

Typ dubbel, 64 bits lang, heeft een groter bereik (*10^+/-308) en 15-cijferige precisie.

Type long double is nominaal 80 bits, hoewel een bepaalde compiler/OS-koppeling het kan opslaan als 12-16 bytes voor uitlijningsdoeleinden. De lange dubbel heeft een exponent die gewoon belachelijk groot is en een precisie van 19 cijfers zou moeten hebben. Microsoft beperkt in hun oneindige wijsheid long double tot 8 bytes, hetzelfde als gewoon double.

Gebruik in het algemeen gewoon type double als je een drijvende-kommawaarde/variabele nodig hebt. Letterlijke drijvende-kommawaarden die in uitdrukkingen worden gebruikt, worden standaard als dubbel behandeld, en de meeste wiskundige functies die drijvende-kommawaarden retourneren, worden verdubbeld. Je bespaart jezelf veel kopzorgen en typecasting als je gewoon dubbel gebruikt.


Antwoord 8, autoriteit 2%

Floats hebben minder precisie dan doubles. Hoewel je het al weet, lees je Wat we moeten weten over drijvende-komma-rekenkundevoor een beter begrip.


Antwoord 9

Er zijn drie typen drijvende komma:

  • zweven
  • dubbel
  • lang dubbel

Een eenvoudig Venn-diagram geeft uitleg over:
De reeks waarden van de typen


Antwoord 10

Als u drijvende-kommagetallen gebruikt, kunt u er niet op vertrouwen dat uw lokale tests exact hetzelfde zijn als de tests die aan de serverzijde worden uitgevoerd. De omgeving en de compiler zijn waarschijnlijk anders op uw lokale systeem en waar de laatste tests worden uitgevoerd. Ik heb dit probleem al vaker gezien in sommige TopCoder-competities, vooral als je twee getallen met drijvende komma probeert te vergelijken.


Antwoord 11

De ingebouwde vergelijkingsbewerkingen verschillen, zoals wanneer u 2 getallen met een drijvende komma vergelijkt, het verschil in gegevenstype (d.w.z. zwevend of dubbel) kan resulteren in verschillende resultaten.


Antwoord 12

Als men met embedded processing werkt, zal uiteindelijk de onderliggende hardware (bijv. FPGA of een specifiek processor-/microcontrollermodel) float optimaal in hardware geïmplementeerd hebben, terwijl double softwareroutines zal gebruiken. Dus als de precisie van een float voldoende is om aan de behoeften te voldoen, zal het programma enkele keren sneller worden uitgevoerd met float dan met dubbel. Zoals vermeld bij andere antwoorden, pas op voor accumulatiefouten.


Antwoord 13

In tegenstelling tot een int(geheel getal), heeft een floateen decimale punt, net als een double.
Maar het verschil tussen de twee is dat een doubletwee keer zo gedetailleerd is als een float, wat betekent dat het het dubbele aantal cijfers achter de komma kan hebben.

Other episodes