Kiedy używasz pływaka, a kiedy używasz podwójnie?

194

Często w moim doświadczeniu programistycznym muszę decydować, czy powinienem używać liczb zmiennoprzecinkowych, czy podwójnych dla moich liczb rzeczywistych. Czasami wybieram float, czasem podwójny, ale tak naprawdę wydaje się to bardziej subiektywne. Gdybym miał stanąć w obronie mojej decyzji, prawdopodobnie nie podałbym rozsądnych powodów.

Kiedy używasz pływaka, a kiedy używasz podwójnego? Czy zawsze używasz podwójnie, tylko gdy występują ograniczenia pamięci, wybierasz float? A może używasz zawsze zmiennoprzecinkowego, chyba że wymaganie precyzji wymaga użycia podwójnego? Czy istnieją jakieś istotne różnice dotyczące złożoności obliczeniowej podstawowej arytmetyki między zmiennoprzecinkową i podwójną? Jakie są zalety i wady używania float lub double? A czy używałeś długiego podwójnego?

Jakub Zaverka
źródło
28
W wielu przypadkach nie chcesz używać żadnego, ale raczej dziesiętnego typu zmiennoprzecinkowego lub stałego. Binarne typy zmiennoprzecinkowe nie mogą dokładnie reprezentować większości miejsc po przecinku.
CodesInChaos
3
Powiązane z tym, co powoduje błędy zaokrąglania zmiennoprzecinkowego? . @CodesInChaos moja odpowiedź tam sugeruje zasoby, które pomogą ci dokonać tego ustalenia, nie ma jednego uniwersalnego rozwiązania.
Mark Booth
Bardzo dobra odpowiedź znaleziona na: Stack Overflow
Haris
5
Co dokładnie rozumiesz przez „ułamki dziesiętne”. Jeśli potrzebujesz dokładnie przedstawić wartości takie jak 0,01 (powiedzmy, dla pieniędzy), to zmiennoprzecinkowa (binarna) nie jest odpowiedzią. Jeśli masz na myśli tylko liczby niecałkowite, to liczba zmiennoprzecinkowa jest prawdopodobnie dobra - ale wtedy „dziesiętne” nie jest najlepszym słowem do opisania tego, czego potrzebujesz.
Keith Thompson
1
Nie zawsze masz wybór. Na przykład na platformie Arduino zarówno double, jak i float oznaczają float. Musisz znaleźć bibliotekę dodatków do obsługi prawdziwych debli.
kiwiron

Odpowiedzi:

187

Domyślnym wyborem dla typu zmiennoprzecinkowego powinna być double. Jest to również typ, który można uzyskać z literałów zmiennoprzecinkowych bez przyrostka lub (w C) standardowych funkcji, które działają na liczb zmiennoprzecinkowych (np exp, sinitp).

float powinien być stosowany tylko wtedy, gdy musisz operować na wielu liczbach zmiennoprzecinkowych (pomyśl w kolejności tysięcy lub więcej), a analiza algorytmu wykazała, że ​​zmniejszony zakres i dokładność nie stanowią problemu.

long doublemożna użyć, jeśli potrzebujesz większego zasięgu lub dokładności niż doublei jeśli zapewnia to na platformie docelowej.

Podsumowując, floati long doublepowinno być zarezerwowane do użytku przez specjalistów, a doubledla „codziennego użytku”.

Bart van Ingen Schenau
źródło
10
Prawdopodobnie nie rozważałbym liczby zmiennoprzecinkowej dla kilku tysięcy wartości, chyba że wystąpiłby problem z wydajnością związany z buforowaniem zmiennoprzecinkowym i przesyłaniem danych. Analiza zwykle wykazuje znaczny koszt, aby wykazać, że liczba zmiennoprzecinkowa jest wystarczająco precyzyjna.
Patricia Shanahan
4
Jako uzupełnienie, jeśli potrzebujesz zgodności z innymi systemami, korzystne może być użycie tych samych typów danych.
zzzzBov
15
Używałbym liczb zmiennoprzecinkowych dla milionów liczb, a nie 1000. Ponadto niektóre procesory graficzne lepiej radzą sobie z liczbami zmiennoprzecinkowymi, w tym przypadku należy używać liczb zmiennoprzecinkowych. W przeciwnym razie używaj podwójnych.
user949300,
4
@PatriciaShanahan - „problem wydajności związany z ...” Dobry przykład to, jeśli planujesz użyć instrukcji SSE2 lub podobnych instrukcji wektorowych, możesz wykonać 4 operacje / wektor w trybie zmiennoprzecinkowym (w porównaniu z 2 na podwójną), co może dać znaczną poprawę prędkości ( połowa operacji i połowa danych do odczytu i zapisu). Może to znacznie obniżyć próg, w którym użycie pływaków staje się atrakcyjne, i warto postarać się rozwiązać problemy numeryczne.
greggo
12
Popieram tę odpowiedź jedną dodatkową radą: kiedy używa się wartości RGB do wyświetlania, dopuszczalne jest użycie float(i czasami półprecyzyjności), ponieważ ani ludzkie oko, wyświetlacz, ani system kolorów nie ma tak wielu bitów precyzji . Ta rada dotyczy np. OpenGL itp. Ta dodatkowa rada nie dotyczy obrazów medycznych, które mają bardziej rygorystyczne wymagania dotyczące precyzji.
rwong
42

Rzadko zdarza się, aby używać kodu zmiennoprzecinkowego zamiast podwójnego kodu w nowoczesnych komputerach. Dodatkowa precyzja zmniejsza (ale nie eliminuje) szansę na błędy zaokrąglania lub inne niedokładności powodujące problemy.

Głównymi powodami, dla których mogę wymyślić float, są:

  1. Przechowujesz duże tablice liczb i musisz zmniejszyć zużycie pamięci programu.
  2. Celujesz w system, który natywnie nie obsługuje zmiennoprzecinkowego podwójnej precyzji. Do niedawna wiele kart graficznych obsługiwało tylko zmiennoprzecinkowe pojedynczej precyzji. Jestem pewien, że istnieje wiele energooszczędnych i wbudowanych procesorów, które również mają ograniczoną obsługę zmiennoprzecinkową.
  3. Celujesz w sprzęt, w którym pojedyncza precyzja jest szybsza niż podwójna precyzja, a twoja aplikacja intensywnie wykorzystuje arytmetykę zmiennoprzecinkową. Uważam, że w nowoczesnych procesorach Intel wszystkie obliczenia zmiennoprzecinkowe są wykonywane z podwójną precyzją, więc niczego tu nie zyskujesz.
  4. Robisz optymalizację niskiego poziomu, na przykład używając specjalnych instrukcji procesora, które działają na wielu liczbach jednocześnie.

Zasadniczo więc, podwójna jest droga, chyba że masz ograniczenia sprzętowe lub jeśli analiza nie wykazała, że ​​przechowywanie liczb podwójnej precyzji znacząco przyczynia się do zużycia pamięci.

użytkownik611910
źródło
2
„Nowoczesne komputery”, czyli procesory Intel x86. Niektóre z maszyn używanych przez Pradawnych zapewniały idealnie odpowiednią precyzję z podstawowym typem pływaka. (CDC 6600 użył słowa 60-bitowe, 48 bitów znormalizowanego zmiennoprzecinkowej mantysy, 12 bitów wykładnika To prawie co daje x86 dla podwójnej precyzji.).
John R. Strohm
@ John.R.Strohm: zgodził się, ale kompilatory C nie istniały na CDC6600. To był Fortran IV ...
Basile Starynkevitch
Przez „nowoczesne komputery” mam na myśli każdy procesor zbudowany w ciągu ostatniej dekady lub dwóch, a właściwie, odkąd standard zmiennoprzecinkowy IEEE został szeroko zaimplementowany. Doskonale zdaję sobie sprawę z tego, że istnieją architektury inne niż x86, i miałem to na uwadze w swojej odpowiedzi - wspomniałem o procesorach graficznych i procesorach wbudowanych, które zwykle nie są procesorami x86.
user611910
To po prostu nieprawda. SSE2 może manipulować 4 zmiennoprzecinkowymi lub 2 podwójnymi w jednej operacji, AVX może manipulować 8 zmiennoprzecinkowymi lub 4 podwójnymi, AVX-512 może manipulować 16 zmiennoprzecinkowymi lub 8 podwójnymi. W przypadku każdego rodzaju obliczeń o wysokiej wydajności matematyka na liczbach zmiennoprzecinkowych powinna być traktowana jako dwukrotność prędkości tych samych operacji na liczbach podwójnych na x86.
Larry Gritz,
1
Jest nawet jeszcze gorzej, ponieważ w pamięci podręcznej procesora można zmieścić dwa razy więcej liczb zmiennoprzecinkowych, a opóźnienie pamięci może być głównym wąskim gardłem w wielu programach. Utrzymywanie całego zestawu pływaków w cieple może być dosłownie o rząd wielkości szybsze niż używanie podwójnych i rozlewanie ich do pamięci RAM.
Larry Gritz,
10

Użyj doubledo wszystkich obliczeń i zmiennych temp. Używaj, floatgdy chcesz zachować tablicę liczb - float[](jeśli dokładność jest wystarczająca), a masz do czynienia z dziesiątkami tysięcy floatliczb.

Wiele / większość funkcji matematycznych lub operatory konwertują / zwracają doublei nie chcesz rzutować liczb z powrotem floatna żadne pośrednie kroki.

Np. Jeśli masz dane wejściowe 100 000 liczb z pliku lub strumienia i musisz je posortować, wpisz liczby w float[].

Fai Ng
źródło
5

Niektóre platformy (ARM Cortex-M2, Cortex-M4 itp.) Nie obsługują podwójnie (zawsze można to sprawdzić w instrukcji obsługi procesora. Jeśli nie ma ostrzeżeń lub błędów kompilacji, nie oznacza to, że kod jest optymalny. podwójne mogą być emulowane.). Dlatego może być konieczne trzymanie się int lub float .

Jeśli tak nie jest, użyłbym podwójnie .

Możesz sprawdzić słynny artykuł D. Goldberga („Co każdy informatyk powinien wiedzieć o arytmetyki zmiennoprzecinkowej”). Powinieneś pomyśleć dwa razy, zanim użyjesz arytmetyki zmiennoprzecinkowej. Istnieje spora szansa, że ​​w ogóle nie są potrzebne w Twojej konkretnej sytuacji.

http://perso.ens-lyon.fr/jean-michel.muller/goldberg.pdf

staroselskii
źródło
3
Na to pytanie już dość dobrze odpowiedział rok temu ... ale w każdym razie powiedziałbym, że za każdym razem, gdy używasz podwójnie na platformach z podwójną precyzją przyspieszania FPU, powinieneś używać go na każdym innym, nawet jeśli to oznacza pozwolenie kompilatorowi na jego emulację zamiast korzystania z FPU tylko z liczbą zmiennoprzecinkową (zwróć uwagę, że FPU nie są również wymagane na wszystkich platformach, w rzeczywistości architektura Cortex-M4 definiuje je jako opcję opcjonalną [czy M2 literówka?] ).
Selali Adobor
Kluczem do tej logiki jest to, że chociaż prawdą jest, że należy znużyć arytmetykę zmiennoprzecinkową i to jest wiele „dziwactw”, zdecydowanie nie przyjmując obecności wsparcia FPU dla podwójnych, oznacza po prostu używać podwójnych zamiast liczb zmiennoprzecinkowych. Pływaki są generalnie szybsze niż podwójne i zajmują mniej pamięci (funkcje FPU są różne). Wielkość zużycia wyklucza przedwczesną optymalizację tego punktu. Podobnie jak fakt, że gra podwójna jest wyraźnie przesadna w przypadku wielu (a może nawet większości) aplikacji. Czy elementy na tej stronie naprawdę muszą mieć swoje względne pozycje i rozmiary obliczane z dokładnością do 13 miejsc po przecinku?
Selali Adobor
2
Dołączając link do strony lub dokumentu poza witryną, skopiuj odpowiednie informacje lub podsumowanie z dokumentu do swojej odpowiedzi. Łącza poza witryną z czasem znikają.
Adam Zuckerman
3

W przypadku rzeczywistych problemów próg próbkowania danych jest ważny podczas odpowiedzi na to pytanie. Podobnie ważna jest również podłoga szumowa. Jeśli jedno z typów danych zostanie przekroczone, zwiększenie precyzji nie przyniesie korzyści.

Większość samplerów w świecie rzeczywistym jest ograniczona do 24-bitowych przetworników cyfrowo-analogowych. Sugerowanie, że 32 bity precyzji w obliczeniach w świecie rzeczywistym powinny być odpowiednie, gdy znaczenie i 24 bity precyzji.

Podwójna precyzja kosztuje 2x pamięć. W związku z tym ograniczenie użycia liczby podwójnej w stosunku do liczb zmiennoprzecinkowych może drastycznie zmniejszyć zużycie pamięci / przepustowość działających aplikacji.

użytkownik3034617
źródło
-3

Wybór zmiennej, która ma być używana między zmiennoprzecinkową i podwójną, zależy od dokładności wymaganych danych. Jeśli wymagana jest niewielka różnica w stosunku do rzeczywistej odpowiedzi, liczba wymaganych miejsc dziesiętnych będzie duża, a zatem dyktuje to użycie podwójnej liczby. Przecięcie części miejsc dziesiętnych zmniejszy dokładność.

David Monyancha
źródło
3
Ta odpowiedź nie dodaje niczego nowego do pytania i nie mówi nic o rzeczywistym użyciu.
Martijn Pieters,
-5

Zwykle używam tego floattypu, gdy nie potrzebuję dużej precyzji - na przykład w przypadku pieniędzy - co jest złe, ale do tego jestem przyzwyczajony.

Z drugiej strony używam, doublegdy potrzebuję większej precyzji, na przykład w przypadku złożonych algorytmów matematycznych.

Standard C99 mówi:

Istnieją trzy typy zmiennoprzecinkowe: zmiennoprzecinkowe, podwójne i długie podwójne. Typ double zapewnia co najmniej tyle samo precyzji co float, a typ double double zapewnia co najmniej tyle samo precyzji co double. Zbiór wartości typu float jest podzbiorem zbioru wartości typu double; zbiór wartości typu double jest podzbiorem zbioru wartości typu double double.

Nigdy tak naprawdę nie korzystałem long double, ale nie używam C / C ++ zbyt często. Zwykle używam dynamicznie pisanych języków, takich jak Python, gdzie nie musisz się przejmować typami.

Aby uzyskać więcej informacji o Double vs Float , zobacz to pytanie w SO .

Addison Montgomery
źródło
25
Wykorzystanie zmiennoprzecinkowe do poważnych obliczeń pieniężnych jest prawdopodobnie błędem.
Bart van Ingen Schenau
17
float jest dokładnie nieodpowiednim rodzajem pieniędzy. Musisz używać najwyższej możliwej precyzji.
ChrisF
8
@BartvanIngenSchenau Zmienny punkt dla pieniędzy jest zwykle w porządku, zmiennoprzecinkowy binarny nie. Na przykład .net Decimaljest typem zmiennoprzecinkowym i zazwyczaj jest dobrym wyborem do obliczania pieniędzy.
CodesInChaos
13
@ChrisF Nie potrzebujesz „wysokiej precyzji” do pieniędzy, potrzebujesz dokładnych wartości.
Sean McSomething
2
@SeanMcSomething - Sprawiedliwy punkt. Jednak zmiennoprzecinkowe są nadal niewłaściwym typem, a biorąc pod uwagę typy zmiennoprzecinkowe dostępne w większości języków, potrzebujesz „wysokiej precyzji”, aby uzyskać „dokładne wartości”.
ChrisF