Mogę wymienić trzy zalety używania double
(lub float
) zamiast decimal
:
- Zużywa mniej pamięci.
- Szybsze, ponieważ operacje matematyczne zmiennoprzecinkowe są natywnie obsługiwane przez procesory.
- Może reprezentować większy zakres liczb.
Ale te zalety wydają się mieć zastosowanie tylko do operacji intensywnie obliczających, takich jak te występujące w oprogramowaniu do modelowania. Oczywiście podwójnych nie należy stosować, gdy wymagana jest precyzja, na przykład obliczenia finansowe. Czy są więc jakieś praktyczne powody, aby kiedykolwiek wybierać double
(lub float
) zamiast decimal
w „normalnych” aplikacjach?
Zredagowano, aby dodać: Dzięki za wszystkie świetne odpowiedzi, nauczyłem się od nich.
Kolejne pytanie: kilka osób stwierdziło, że liczby podwójne mogą dokładniej przedstawiać liczby rzeczywiste. Gdy zostaną zadeklarowane, sądzę, że zwykle również bardziej dokładnie je przedstawiają. Ale czy to prawda, że dokładność może się zmniejszyć (czasem znacznie) podczas wykonywania operacji zmiennoprzecinkowych?
Odpowiedzi:
Myślę, że całkiem dobrze podsumowałeś zalety. Brakuje Ci jednak jednego punktu. Ten
decimal
typ jest bardziej dokładny w reprezentowaniu liczb podstawowych 10 (np. Tych używanych w obliczeniach walutowych / finansowych). Ogólnie rzecz biorąc, tendouble
typ będzie oferował co najmniej tak dużą precyzję (ktoś mnie poprawi, jeśli się mylę) i zdecydowanie większą prędkość dla dowolnych liczb rzeczywistych. Prosty wniosek jest następujący: rozważając, którego użyć, zawsze używaj,double
chyba że potrzebujeszbase 10
dokładności, któradecimal
oferuje.Edytować:
Jeśli chodzi o twoje dodatkowe pytanie dotyczące zmniejszenia dokładności liczb zmiennoprzecinkowych po operacjach, jest to nieco bardziej subtelna kwestia. Rzeczywiście, precyzja (używam terminu zamiennie dla dokładności tutaj) stopniowo spada po każdej operacji. Wynika to z dwóch powodów:
We wszystkich przypadkach, jeśli chcesz porównać dwie liczby zmiennoprzecinkowe, które teoretycznie powinny być równoważne (ale zostały osiągnięte przy użyciu różnych obliczeń), musisz pozwolić na pewien stopień tolerancji (jak bardzo się różni, ale zazwyczaj jest bardzo mały) .
Aby uzyskać bardziej szczegółowy przegląd konkretnych przypadków, w których można wprowadzić błędy w dokładności, zobacz sekcję Dokładność w artykule w Wikipedii . Na koniec, jeśli chcesz poważnie dogłębnej (i matematycznej) dyskusji na temat liczb zmiennoprzecinkowych / operacji na poziomie maszyny, spróbuj przeczytać często cytowany artykuł Co każdy informatyk powinien wiedzieć o arytmetyki zmiennoprzecinkowej .
źródło
double
. Współczesne komputery nadal będą drukować poprawną wartość, ale tylko dlatego, że „zgadują” wynik - nie dlatego, że tak naprawdę jest poprawnie wyrażona.Decimal
typ ma 93-bitową precyzję w mantysie, w porównaniu z około 52 dladouble
. Chciałbym jednak, żeby Microsoft wspierał 80-bitowy format IEEE, nawet jeśli musiałby zostać uzupełniony do 16 bajtów; pozwoliłby na większy zasięgdouble
lubDecimal
, znacznie lepszą prędkośćDecimal
, wsparcie dla operacji transcendentalnych (np. sin (x), log (x) itd.) i precyzję, która, choć nie tak dobra, jakDecimal
byłaby znacznie lepsza niżdouble
.Wydaje ci się, że masz zalety korzystania z typu zmiennoprzecinkowego. Zwykle projektuję dziesiętne we wszystkich przypadkach i polegam na profilerze, który informuje mnie, czy operacje dziesiętne powodują wąskie gardła lub spowolnienia. W takich przypadkach „rzucę” w dół, aby podwajać lub unosić, ale robię to tylko wewnętrznie i ostrożnie staram się zarządzać utratą precyzji, ograniczając liczbę znaczących cyfr w wykonywanej operacji matematycznej.
Ogólnie rzecz biorąc, jeśli twoja wartość jest przejściowa (nie jest ponownie wykorzystywana), możesz bezpiecznie używać typu zmiennoprzecinkowego. Rzeczywistym problemem związanym z typami zmiennoprzecinkowymi są następujące trzy scenariusze.
123456789.1 * .000000000000000987654321
)EDYTOWAĆ
Zgodnie z dokumentacją referencyjną dotyczącą liczb dziesiętnych w C # :
Aby wyjaśnić moje powyższe stwierdzenie:
Pracowałem tylko w branżach, w których dziesiętne są korzystne. Jeśli pracujesz nad silnikami fizyki lub graficznymi, prawdopodobnie bardziej korzystne jest zaprojektowanie typu zmiennoprzecinkowego (zmiennoprzecinkowego lub podwójnego).
Dziesiętny nie jest nieskończenie dokładny (nie można przedstawić nieskończonej precyzji dla niecałkowitej w prymitywnym typie danych), ale jest znacznie bardziej precyzyjny niż dwukrotność:
EDYCJA 2
W odpowiedzi na komentarz Konrada Rudolfa punkt 1 (powyżej) jest zdecydowanie poprawny. Agregacja niedokładności rzeczywiście się pogarsza. Zobacz poniższy kod dla przykładu:
To powoduje, że:
Jak widać, mimo że dodajemy z tej samej stałej źródłowej, wyniki podwójności są mniej precyzyjne (chociaż prawdopodobnie będą się zaokrąglały poprawnie), a liczba zmiennoprzecinkowa jest znacznie mniej dokładna, do tego stopnia, że została zredukowana do tylko dwie cyfry znaczące.
źródło
Single: 667660.400000000000
a uzyskano wartość dziesiętnąDecimal: 666666.7000000000
. Wartość zmiennoprzecinkowa jest nieco mniejsza niż tysiąc ponad prawidłową wartość.Użyj wartości dziesiętnych dla 10 podstawowych wartości, np. Obliczeń finansowych, jak sugerowali inni.
Ale podwójna jest generalnie dokładniejsza dla dowolnych obliczonych wartości.
Na przykład, jeśli chcesz obliczyć wagę każdej linii w portfelu, użyj podwójnie, ponieważ wynik będzie prawie równy 100%.
W poniższym przykładzie doubleResult jest bliższy 1 niż decimalResult:
Ponownie na przykładzie portfela:
Wartość rynkowa każdej linii w portfelu jest wartością pieniężną i prawdopodobnie najlepiej byłaby reprezentowana jako dziesiętna.
Waga każdej linii w portfelu (= wartość rynkowa / SUMA (wartość rynkowa)) jest zwykle lepiej reprezentowana jako podwójna.
źródło
Użyj podwójnego lub zmiennoprzecinkowego, gdy nie potrzebujesz precyzji, na przykład w napisanej przeze mnie platformówce użyłem zmiennoprzecinkowego do przechowywania prędkości gracza. Oczywiście nie potrzebuję tutaj super precyzji, ponieważ w końcu wybieram Int do rysowania na ekranie.
źródło
W niektórych rachunkach rozważ możliwość zastosowania typów integralnych zamiast lub w połączeniu. Na przykład, powiedzmy, że zasady, którymi się kierujesz, wymagają, aby każdy wynik obliczeń był przenoszony co najmniej o 6 miejsc po przecinku, a wynik końcowy zostanie zaokrąglony do najbliższego centa.
Obliczenie 1/6 ze 100 $ daje 16,666666666666666 ... więc wartość przeprowadzona w arkuszu wyniesie 16,6666667. Zarówno podwójne, jak i dziesiętne powinny dać wynik dokładnie do 6 miejsc po przecinku. Możemy jednak uniknąć błędu skumulowanego, przenosząc wynik jako liczbę całkowitą 16666667. Każde kolejne obliczenie można wykonać z tą samą precyzją i przenieść w podobny sposób. Kontynuując przykład, obliczam podatek od sprzedaży w Teksasie od tej kwoty (16666667 * .0825 = 1375000). Dodanie dwóch (jest to krótki arkusz) 1666667 + 1375000 = 18041667. Przesunięcie punktu dziesiętnego z powrotem daje nam 18.041667, czyli 18,04 USD.
Chociaż ten krótki przykład nie dałby skumulowanego błędu przy użyciu podwójnej lub dziesiętnej, dość łatwo jest pokazać przypadki, w których zwykłe obliczenie podwójnej lub dziesiętnej i przeniesienie spowodowałoby znaczny błąd. Jeśli reguły, na których działasz, wymagają ograniczonej liczby miejsc po przecinku, przechowywanie każdej wartości jako liczby całkowitej przez pomnożenie przez 10 ^ (wymagany # miejsca po przecinku), a następnie podzielenie przez 10 ^ (wymagany # miejsc po przecinku), aby uzyskać rzeczywistą wartość pozwoli uniknąć błędu skumulowanego.
W sytuacjach, w których nie występują ułamki grosza (na przykład automat), nie ma powodu, aby w ogóle używać typów niezintegrowanych. Pomyśl o tym jak o liczeniu groszy, a nie o dolarach. Widziałem kod, w którym każde obliczenie obejmowało tylko całe grosze, ale użycie podwójnego doprowadziło do błędów! Matematyka tylko liczby całkowite usunęła problem. Tak więc moja niekonwencjonalna odpowiedź brzmi, gdy jest to możliwe, rezygnuje zarówno z podwójnej, jak i dziesiętnej.
źródło
Jeśli potrzebujesz interpolacji binarnej z innymi językami lub platformami, może być konieczne użycie zmiennoprzecinkowe lub podwójne, które są znormalizowane.
źródło
Uwaga: ten post jest oparty na informacjach o możliwościach typu dziesiętnego z http://csharpindepth.com/Articles/General/Decimal.aspx i mojej własnej interpretacji tego, co to znaczy. Zakładam, że Double jest normalną podwójną precyzją IEEE.
Uwaga 2: najmniejsze i największe w tym poście odnoszą się do wielkości liczby.
Zalety „dziesiętnych”.
Wady dziesiętne
Uważam, że powinieneś domyślnie używać „dziesiętnego” do pracy pieniężnej i innych przypadków, w których dokładne dopasowanie do ludzkich obliczeń jest ważne i że powinieneś używać podwójnego jako domyślnego wyboru przez resztę czasu.
źródło
Zależy od tego, czego potrzebujesz.
Ponieważ zmiennoprzecinkowe i podwójne są binarnymi typami danych, masz pewne trudności i błędy na drodze w liczbach rund, więc na przykład podwójny zaokrągliby 0,1 do 0,100000001490116, podwójny również zaokrągliby 1/3 do 0.33333334326441. Mówiąc prosto, nie wszystkie liczby rzeczywiste mają dokładne odwzorowanie w typach podwójnych
Na szczęście C # obsługuje również tak zwaną arytmetykę zmiennoprzecinkową dziesiętną, w której liczby są reprezentowane przez dziesiętny system liczbowy, a nie przez system binarny. Zatem dziesiętna arytmetyka zmiennoprzecinkowa nie traci dokładności podczas przechowywania i przetwarzania liczb zmiennoprzecinkowych. Dzięki temu doskonale nadaje się do obliczeń, w których wymagany jest wysoki poziom dokładności.
źródło
Użyj zmiennoprzecinkowych, jeśli cenisz wydajność nad poprawnością.
źródło
Wybierz typ w funkcji swojej aplikacji. Jeśli potrzebujesz precyzji jak w analizie finansowej, odpowiedziałeś na swoje pytanie. Ale jeśli twoja aplikacja może zostać rozstrzygnięta z oszacowaniem, twój ok jest podwójny.
Czy Twoja aplikacja wymaga szybkich obliczeń, czy będzie miał cały czas na świecie, aby udzielić odpowiedzi? To zależy od rodzaju aplikacji.
Głodny grafik? wystarczy float lub double. Analiza danych finansowych, meteor uderzający w planetę w pewien rodzaj precyzji? Te wymagałyby trochę precyzji :)
źródło
Dziesiętny ma szersze bajty, podwójne jest natywnie obsługiwane przez procesor. Dziesiętny to podstawa-10, więc podczas obliczania dziesiętnego następuje konwersja dziesiętna na podwójną.
Należy pamiętać, .NET CLR obsługuje tylko Math.Pow (podwójne, podwójne). Dziesiętny nie jest obsługiwany.
.NET Framework 4
źródło
Podwójne wartości będą domyślnie serializowane do notacji naukowej, jeśli notacja ta jest krótsza niż wyświetlanie dziesiętne. (np. 0,00000003 to 3e-8) Wartości dziesiętne nigdy nie będą serializowane do notacji naukowej. Może to być brane pod uwagę przy serializacji do celów konsumpcji przez podmiot zewnętrzny.
źródło