Duża różnica.
Jak sama nazwa wskazuje, a double
ma 2x precyzję [1] . Ogólnie rzecz biorąc a ma 15 cyfr dziesiętnych precyzji, a ma 7.float
double
float
Oto jak obliczana jest liczba cyfr:
double
ma 52 bity mantysy + 1 ukryty bit: log (2 53 ) ÷ log (10) = 15,95 cyfr
float
ma 23 bity mantysy + 1 ukryty bit: log (2 24 ) ÷ log (10) = 7,22 cyfry
Ta utrata precyzji może prowadzić do gromadzenia większych błędów skracania, gdy wykonywane są powtarzane obliczenia, np
float a = 1.f / 81;
float b = 0;
for (int i = 0; i < 729; ++ i)
b += a;
printf("%.7g\n", b); // prints 9.000023
podczas
double a = 1.0 / 81;
double b = 0;
for (int i = 0; i < 729; ++ i)
b += a;
printf("%.15g\n", b); // prints 8.99999999999996
Ponadto maksymalna wartość liczby zmiennoprzecinkowej wynosi około 3e38
, ale wartość podwójna wynosi około 1.7e308
, więc użycie float
może osiągnąć „nieskończoność” (tj. Specjalną liczbę zmiennoprzecinkową) o wiele łatwiej niż w double
przypadku czegoś prostego, np. Obliczenie silni 60.
Podczas testowania może kilka przypadków testowych zawiera te ogromne liczby, co może spowodować awarię programów, jeśli używasz liczb zmiennoprzecinkowych.
Oczywiście czasami nawet double
nie jest wystarczająco dokładna, dlatego czasami mamy long double
[1] (powyższy przykład podaje 9.000000000000000066 na Macu), ale wszystkie typy zmiennoprzecinkowe cierpią na błędy zaokrąglania , więc jeśli precyzja jest bardzo ważna (np. Pieniądze przetwarzanie) należy użyć int
lub klasy ułamkowej.
Ponadto nie należy używać +=
do sumowania wielu liczb zmiennoprzecinkowych, ponieważ błędy kumulują się szybko. Jeśli używasz Pythona, użyj fsum
. W przeciwnym razie spróbuj zaimplementować algorytm sumowania Kahana .
[1]: C i C ++ normy nie określają reprezentację float
, double
i long double
. Możliwe jest, że wszystkie trzy zostaną zaimplementowane jako podwójna precyzja IEEE. Niemniej jednak dla większości architektur (gcc, MSVC; x86, x64, ARM) float
jest rzeczywiście liczbą zmiennoprzecinkową pojedynczej precyzji IEEE (binary32) i double
jest liczbą zmiennoprzecinkową podwójnej precyzji IEEE (binary64).
Oto, co mówią standardowe normy C99 (ISO-IEC 9899 6.2.5 §10) lub C ++ 2003 (ISO-IEC 14882-2003 3.1.9 §8):
Standard C ++ dodaje:
Proponuję rzucić okiem na to, co każdy informatyk powinien wiedzieć o arytmetyki zmiennoprzecinkowej, która obejmuje głębię standardu zmiennoprzecinkowego IEEE. Dowiesz się o szczegółach reprezentacji i zrozumiesz, że istnieje kompromis między wielkością a precyzją. Precyzja reprezentacji zmiennoprzecinkowej rośnie wraz ze spadkiem wielkości, stąd liczby zmiennoprzecinkowe od -1 do 1 to te z największą precyzją.
źródło
Biorąc pod uwagę równanie kwadratowe: x 2 - 4,0000000 x + 3,9999999 = 0, dokładne pierwiastki do 10 cyfr znaczących to: r 1 = 2.000316228 i r 2 = 1.999683772.
Za pomocą
float
idouble
możemy napisać program testowy:Uruchomienie programu daje mi:
Zauważ, że liczby nie są duże, ale nadal otrzymujesz efekty anulowania za pomocą
float
.(W rzeczywistości powyższe nie jest najlepszym sposobem rozwiązywania równań kwadratowych za pomocą liczb zmiennoprzecinkowych o pojedynczej lub podwójnej precyzji, ale odpowiedź pozostaje niezmieniona, nawet jeśli stosuje się bardziej stabilną metodę ).
źródło
źródło
Rozmiar liczb biorących udział w obliczeniach zmiennoprzecinkowych nie jest najważniejszy. Ważne jest wykonanie obliczeń.
Zasadniczo, jeśli wykonujesz obliczenia, a wynikiem jest liczba niewymierna lub powtarzająca się liczba dziesiętna, wystąpią błędy zaokrąglania, gdy liczba ta zostanie zmiażdżona w używanej strukturze danych o skończonym rozmiarze. Ponieważ double jest dwa razy większy niż liczba zmiennoprzecinkowa, błąd zaokrąglenia będzie znacznie mniejszy.
Testy mogą w szczególności używać liczb, które spowodowałyby tego rodzaju błąd, a zatem przetestowały, czy użyłeś odpowiedniego typu w kodzie.
źródło
Typ float, o długości 32 bitów, ma dokładność 7 cyfr. Chociaż może przechowywać wartości o bardzo dużym lub bardzo małym zakresie (+/- 3,4 * 10 ^ 38 lub * 10 ^ -38), ma tylko 7 cyfr znaczących.
Typ double, 64-bitowy, ma większy zakres (* 10 ^ + / - 308) i 15 cyfrową dokładność.
Typ double double ma nominalnie 80 bitów, chociaż dane parowanie kompilatora / systemu operacyjnego może przechowywać go jako 12-16 bajtów dla celów wyrównania. Długi podwójny ma wykładnik, który jest absurdalnie ogromny i powinien mieć 19-cyfrową precyzję. Microsoft, w swojej nieskończonej mądrości, ogranicza długi podwójny do 8 bajtów, taki sam jak zwykły podwójny.
Ogólnie rzecz biorąc, po prostu użyj typu double, gdy potrzebujesz wartości zmiennoprzecinkowej / zmiennej. Dosłowne wartości zmiennoprzecinkowe używane w wyrażeniach będą domyślnie traktowane jako liczby podwójne, a większość funkcji matematycznych zwracających wartości zmiennoprzecinkowe zwraca liczbę podwójną. Zaoszczędzisz sobie wielu bólów głowy i rzutów, jeśli użyjesz podwójnie.
źródło
Właśnie natrafiłem na błąd, który wymyślił mi wieczność i potencjalnie może dać dobry przykład precyzji float.
Dane wyjściowe to
Jak widać po 0,83, precyzja znacznie spada.
Jednak jeśli skonfiguruję
t
jako podwójny, taki problem się nie stanie.Pięć godzin zajęło mi zrozumienie tego drobnego błędu, który zrujnował mój program.
źródło
double
nie jest tutaj dobrym rozwiązaniem. Służyint
do liczenia i wewnętrznego mnożenia w celu uzyskania wartości zmiennoprzecinkowej.Spławiki mają mniejszą precyzję niż podwójne. Chociaż już wiesz, przeczytaj Co powinniśmy wiedzieć o arytmetyki zmiennoprzecinkowej dla lepszego zrozumienia.
źródło
Korzystając z liczb zmiennoprzecinkowych, nie można ufać, że testy lokalne będą dokładnie takie same, jak testy wykonywane po stronie serwera. Środowisko i kompilator prawdopodobnie różnią się w twoim systemie lokalnym i gdzie przeprowadzane są końcowe testy. Widziałem ten problem wiele razy wcześniej w niektórych konkursach TopCoder, zwłaszcza jeśli próbujesz porównać dwie liczby zmiennoprzecinkowe.
źródło
Wbudowane operacje porównania różnią się, ponieważ przy porównywaniu 2 liczb z liczbą zmiennoprzecinkową różnica w typie danych (tj. Zmiennoprzecinkowa lub podwójna) może powodować różne wyniki.
źródło
Jeśli ktoś pracuje z wbudowanym przetwarzaniem, ostatecznie sprzęt bazowy (np. FPGA lub jakiś konkretny model procesora / mikrokontrolera) będzie optymalnie zaimplementowany w sprzęcie, podczas gdy podwójne będą korzystać z procedur programowych. Więc jeśli dokładność liczby zmiennoprzecinkowej jest wystarczająca do zaspokojenia potrzeb, program będzie działał kilka razy szybciej z liczbą zmiennoprzecinkową niż dwukrotnie. Jak zauważono w innych odpowiedziach, uważaj na błędy akumulacji.
źródło
W przeciwieństwie do
int
(liczby całkowitej), afloat
ma przecinek dziesiętny, a więc może równieżdouble
. Różnica między nimi polega na tym, że adouble
jest dwa razy bardziej szczegółowe niż afloat
, co oznacza, że może mieć podwojoną liczbę liczb po przecinku.źródło