Jakie są zalety i wady używania jednego zamiast drugiego w C ++?
Jakie są zalety i wady używania jednego zamiast drugiego w C ++?
Odpowiedzi:
Jeśli chcesz poznać prawdziwą odpowiedź, przeczytaj artykuł Co każdy informatyk powinien wiedzieć o arytmetyce zmiennoprzecinkowej .
Krótko mówiąc, chociaż double
pozwala na większą precyzję w reprezentacji, dla niektórych obliczeń spowodowałaby większe błędy . „Właściwy” wybór to: użyj tyle precyzji, ile potrzebujesz, ale nie więcej, i wybierz właściwy algorytm .
Wiele kompilatorów i tak wykonuje rozszerzone obliczenia zmiennoprzecinkowe w trybie „nieścisłym” (tj. Używa szerszego typu zmiennoprzecinkowego dostępnego w sprzęcie, np. Zmiennoprzecinkowy 80-bitowy i 128-bitowy zmiennoprzecinkowy). W praktyce nie widać prawie żadnej różnicy w szybkości - i tak są to rdzenni mieszkańcy sprzętu.
double
- jest to w większości przypadków bezpieczniejsze.
Jeśli nie masz konkretnego powodu, aby postąpić inaczej, użyj double.
Być może zaskakujące jest to, że jest to double, a nie float, czyli „normalny” typ zmiennoprzecinkowy w C (i C ++). Standardowe funkcje matematyczne, takie jak sin i log, przyjmują podwojenie jako argumenty i zwracają podwojenie. Normalny literał zmiennoprzecinkowy, tak jak podczas pisania 3.14 w programie, ma typ double. Nie unosi się.
Na typowych nowoczesnych komputerach podwójne mogą być równie szybkie, jak pływające lub nawet szybsze, więc wydajność zwykle nie jest czynnikiem do rozważenia, nawet w przypadku dużych obliczeń. (A to musiałyby być duże obliczenia, inaczej wydajność nie powinna przychodzić ci do głowy. Mój nowy komputer stacjonarny i7 może wykonać sześć miliardów razy podwojenie w ciągu jednej sekundy.)
Na to pytanie nie można odpowiedzieć, ponieważ nie ma kontekstu w pytaniu. Oto kilka rzeczy, które mogą wpłynąć na wybór:
Implementacja w kompilatorze zmiennych typu float, double i long double. Standard C ++ stwierdza:
Istnieją trzy typy zmiennoprzecinkowe: zmiennoprzecinkowe, podwójne i długie podwójne. Typ double zapewnia co najmniej taką samą precyzję jak zmiennoprzecinkowy, a typ long double zapewnia co najmniej taką samą precyzję jak double.
Zatem wszystkie trzy mogą mieć ten sam rozmiar w pamięci.
Obecność FPU. Nie wszystkie procesory mają jednostki FPU i czasami typy zmiennoprzecinkowe są emulowane, a czasami typy zmiennoprzecinkowe po prostu nie są obsługiwane.
Architektura FPU. FPU IA32 jest wewnętrznie 80-bitowe - 32-bitowe i 64-bitowe zmiennoprzecinkowe są rozszerzane do 80-bitowych przy obciążeniu i zmniejszane w magazynie. Istnieje również SIMD, który może wykonywać cztery 32-bitowe zmiennoprzecinkowe lub dwa 64-bitowe zmiennoprzecinkowe równolegle. Użycie SIMD nie jest zdefiniowane w standardzie, więc wymagałoby kompilatora, który przeprowadza bardziej złożoną analizę w celu ustalenia, czy można użyć SIMD, lub wymaga użycia funkcji specjalnych (bibliotek lub wewnętrznych). Konsekwencją 80-bitowego formatu wewnętrznego jest to, że można uzyskać nieco inne wyniki w zależności od tego, jak często dane są zapisywane w pamięci RAM (co powoduje utratę precyzji). Z tego powodu kompilatory nie optymalizują szczególnie dobrze kodu zmiennoprzecinkowego.
Przepustowość pamięci. Jeśli double wymaga więcej miejsca niż float, odczyt danych zajmie więcej czasu. To naiwna odpowiedź. Na nowoczesnym IA32 wszystko zależy od tego, skąd pochodzą dane. Jeśli jest w pamięci podręcznej L1, obciążenie jest pomijalne, pod warunkiem, że dane pochodzą z jednej linii pamięci podręcznej. Jeśli obejmuje więcej niż jedną linię pamięci podręcznej, jest niewielki narzut. Jeśli jest z L2, zajmuje to trochę więcej czasu, jeśli jest w pamięci RAM, to nadal jest dłużej i wreszcie, jeśli jest na dysku, to ogromny czas. Zatem wybór liczby zmiennoprzecinkowej lub podwójnej jest mniej ważny niż sposób wykorzystania danych. Jeśli chcesz wykonać małe obliczenia na wielu sekwencyjnych danych, preferowany jest mały typ danych. Wykonywanie wielu obliczeń na małym zestawie danych umożliwiłoby użycie większych typów danych z istotnym skutkiem. Jeśli ty' ponowne uzyskiwanie dostępu do danych w sposób bardzo losowy, wtedy wybór rozmiaru danych nie ma znaczenia - dane są ładowane w stronach / wierszach pamięci podręcznej. Więc nawet jeśli chcesz tylko bajt z pamięci RAM, możesz przesłać 32 bajty (jest to bardzo zależne od architektury systemu). Ponadto procesor / FPU może być superskalarny (inaczej potokowy). Tak więc, nawet jeśli ładowanie może zająć kilka cykli, procesor / FPU może być zajęty robieniem czegoś innego (na przykład mnożenia), co do pewnego stopnia ukrywa czas ładowania.
Standard nie wymusza żadnego konkretnego formatu dla wartości zmiennoprzecinkowych.
Jeśli masz specyfikację, poprowadzi Cię ona do optymalnego wyboru. W przeciwnym razie zależy od doświadczenia, czego użyć.
Double jest bardziej precyzyjne, ale jest zakodowane na 8 bajtach. float to tylko 4 bajty, więc mniej miejsca i mniejsza precyzja.
Powinieneś być bardzo ostrożny, jeśli w swojej aplikacji masz double i float. Miałem z tego powodu błąd w przeszłości. Jedna część kodu korzystała z float, podczas gdy reszta kodu korzystała z double. Kopiowanie double do float, a następnie float do double może spowodować błąd precyzji, który może mieć duży wpływ. W moim przypadku była to fabryka chemiczna ... mam nadzieję, że nie miało to dramatycznych konsekwencji :)
Myślę, że to właśnie z powodu tego rodzaju błędu rakieta Ariane 6 eksplodowała kilka lat temu !!!
Zastanów się dokładnie, jaki typ zmiennej ma być użyty
Zależy to od tego, jak kompilator implementuje double. Dopuszczalne jest, aby double i float były tego samego typu (i tak jest w niektórych systemach).
Biorąc to pod uwagę, jeśli rzeczywiście są różne, główną kwestią jest precyzja. Podwójna ma znacznie większą precyzję ze względu na różnicę w rozmiarze. Jeśli liczby, których używasz, zwykle przekraczają wartość zmiennoprzecinkową, użyj podwójnej.
Kilka innych osób wspomniało o problemach z wydajnością. To byłoby dokładnie ostatnie na mojej liście rozważań. Poprawność powinna być twoją najważniejszą kwestią.
Użyj dowolnej precyzji, która jest wymagana, aby uzyskać odpowiednie wyniki . Jeśli zauważysz, że Twój kod nie działa tak dobrze, jak byś chciał (użyłeś poprawnego profilowania?), Spójrz na:
Myślę, że niezależnie od różnic (które jak wszyscy podkreślają, pływaki zajmują mniej miejsca i są generalnie szybsze) ... czy ktoś kiedykolwiek miał problemy z wydajnością, używając double? Mówię, używaj podwójnego ... a jeśli później zdecydujesz "wow, to jest naprawdę wolne" ... znajdź wąskie gardło wydajności (co prawdopodobnie nie jest faktem, że użyłeś podwójnego). WTEDY, jeśli nadal jest dla Ciebie za wolny, zobacz, gdzie możesz poświęcić trochę precyzji i użyć spławika.
Główną różnicą między float a double jest precyzja. Wikipedia zawiera więcej informacji o pojedynczej precyzji (zmiennoprzecinkowej) i podwójnej precyzji .
W dużej mierze zależy to od procesora, a najbardziej oczywiste są kompromisy między precyzją a pamięcią. Przy GB pamięci RAM pamięć nie stanowi większego problemu, więc generalnie lepiej jest używać double
s.
Jeśli chodzi o wydajność, zależy to w dużym stopniu od procesora. float
s zwykle uzyskuje lepszą wydajność niż double
s na komputerze 32-bitowym. Na 64 bitach double
s są czasami szybsze, ponieważ jest to (zwykle) rozmiar natywny. Jednak to, co będzie miało znacznie większe znaczenie niż wybór typów danych, to to, czy możesz skorzystać z instrukcji SIMD na swoim procesorze.
double ma większą precyzję, podczas gdy zmiennoprzecinkowe zajmują mniej pamięci i są szybsze. Ogólnie rzecz biorąc, powinieneś używać float, chyba że masz przypadek, w którym nie jest wystarczająco dokładny.