Kiedy należy używać podwójnego zamiast dziesiętnego?

Mogę wymienić trzy zalety używania double (LUB float) zamiast decimal:

  1. zużywa mniej pamięci.
  2. szybsze, ponieważ operacje matematyczne zmiennoprzecinkowe są natywnie obsługiwane przez procesory.
  3. może reprezentować większy zakres liczb.

Ale te zalety wydają się dotyczyć tylko intensywnych operacji obliczeniowych, takich jak te Znalezione w oprogramowaniu do modelowania. Oczywiście, podwajanie nie powinno być stosowane, gdy wymagana jest precyzja, np. obliczenia finansowe. Czy są jakieś praktyczne powody, aby wybrać double (LUB float) zamiast decimal w "normalnych" aplikacjach?

Edited to add: Dzięki za wszystkie świetne odpowiedzi, nauczyłem się od nich.

Jeszcze jedno pytanie: kilka osób stwierdziło, że liczby podwójne mogą dokładniej reprezentować liczby rzeczywiste. Kiedy się deklaruje, myślę, że zwykle dokładniej je reprezentują. Ale czy jest to prawdziwe stwierdzenie, że dokładność może się zmniejszyć (czasami znacznie) przy pływaniu operacje punktowe są wykonywane?

Author: HerbalMart, 2009-04-29

12 answers

Myślę, że dobrze podsumowałeś zalety. Brakuje ci jednak jednego punktu. Na decimal typ jest dokładniejszy tylko przy reprezentowaniu liczb bazowych 10 (np. stosowanych w obliczeniach walutowych/finansowych). Ogólnie rzecz biorąc,double typ ma oferować co najmniej taką samą precyzję (ktoś mnie poprawi, jeśli się mylę) i zdecydowanie większą prędkość dla dowolnych liczb rzeczywistych. Prosty wniosek brzmi: rozważając, którego użyć, Zawsze używaj double, chyba że potrzebujesz dokładności base 10, którą oferuje decimal.

Edit:

Jeśli chodzi o dodatkowe pytanie dotyczące spadku dokładności liczb zmiennoprzecinkowych po operacjach, jest to nieco bardziej subtelny problem. Rzeczywiście, precyzja (używam tego terminu zamiennie dla dokładności tutaj) będzie stopniowo zmniejszać się po każdej operacji jest wykonywana. Wynika to z dwóch powodów:

  1. fakt, że pewne liczby (najczęściej dziesiętne) nie mogą być naprawdę reprezentowane w postać zmiennoprzecinkowa
  2. pojawiają się błędy zaokrąglania, tak jakbyś wykonywał obliczenia ręcznie. To zależy w dużym stopniu od kontekstu (ile operacji wykonujesz), czy te błędy są wystarczająco znaczące, aby uzasadnić wiele przemyśleń.

We wszystkich przypadkach, jeśli chcesz porównać dwie liczby zmiennoprzecinkowe, które teoretycznie powinny być równoważne (ale zostały uzyskane przy użyciu różnych obliczeń), musisz zezwolić na pewien stopień tolerancji (jak bardzo się zmienia, ale jest zazwyczaj bardzo mały).

Aby uzyskać bardziej szczegółowy przegląd szczególnych przypadków, w których błędy w dokładności mogą być wprowadzone, zobacz sekcję dokładność artykuł 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 arytmetyce zmiennoprzecinkowej.

 288
Author: Noldorin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-20 12:39:12

Wydajesz się być na miejscu z korzyściami płynącymi z używania typu zmiennoprzecinkowego. Zazwyczaj projektuję dla dziesiętnych we wszystkich przypadkach i polegam na profilerze, który da mi znać, czy operacje na dziesiętnych powodują wąskie gardła lub spowolnienia. W takich przypadkach, będę "down cast" podwoić lub float, ale tylko zrobić to wewnętrznie, i ostrożnie spróbować zarządzać straty precyzji poprzez ograniczenie liczby znaczących cyfr w operacji matematycznej jest wykonywana.

Ogólnie, jeśli wartość jest przejściowa (nie reused), można bezpiecznie używać typu zmiennoprzecinkowego. Prawdziwym problemem typów zmiennoprzecinkowych są następujące trzy scenariusze.

  1. agregujesz wartości zmiennoprzecinkowe (w tym przypadku związek błędów precyzji)
  2. budujesz wartości na podstawie wartości zmiennoprzecinkowej (na przykład w algorytmie rekurencyjnym)
  3. wykonujesz matematykę z bardzo dużą liczbą cyfr znaczących (na przykład, 123456789.1 * .000000000000000987654321)

EDIT

Zgodnie z reference documentation on C# :

Decimal słowo kluczowe oznacza 128-bitowy typ danych. W porównaniu do typy zmiennoprzecinkowe, Typ dziesiętny ma większą precyzję i mniejszą zakres, co sprawia, że nadaje się do obliczenia finansowe i monetarne.

Aby wyjaśnić moje powyższe stwierdzenie:

Mam tendencję do projektowania dla dziesiętnych we wszystkich przypadków i polegają na profilerze, który pozwoli ja wiem czy operacje na decimal są powodujące wąskie gardła lub spowolnienia.

Pracowałem tylko w branżach, w których liczby dziesiętne są korzystne. Jeśli pracujesz nad phsyics lub silnikami graficznymi, prawdopodobnie bardziej korzystne jest projektowanie dla typu zmiennoprzecinkowego (float lub double).

Decimal nie jest nieskończenie precyzyjne (niemożliwe jest przedstawienie nieskończonej precyzji dla nieintegralnych w prymitywnym typie danych), ale jest znacznie dokładniejsze niż double:

  • dziesiętne = 28-29 znaczące cyfry
  • double = 15-16 cyfr znaczących
  • float = 7 cyfr

EDIT 2

W odpowiedzi nakomentarz Konrada Rudolfa punkt # 1 (powyżej) jest zdecydowanie poprawny. Agregacja nieprecyzyjności rzeczywiście się łączy. Zobacz poniższy kod dla przykładu:

private const float THREE_FIFTHS = 3f / 5f;
private const int ONE_MILLION = 1000000;

public static void Main(string[] args)
{
    Console.WriteLine("Three Fifths: {0}", THREE_FIFTHS.ToString("F10"));
    float asSingle = 0f;
    double asDouble = 0d;
    decimal asDecimal = 0M;

    for (int i = 0; i < ONE_MILLION; i++)
    {
        asSingle += THREE_FIFTHS;
        asDouble += THREE_FIFTHS;
        asDecimal += (decimal) THREE_FIFTHS;
    }
    Console.WriteLine("Six Hundred Thousand: {0:F10}", THREE_FIFTHS * ONE_MILLION);
    Console.WriteLine("Single: {0}", asSingle.ToString("F10"));
    Console.WriteLine("Double: {0}", asDouble.ToString("F10"));
    Console.WriteLine("Decimal: {0}", asDecimal.ToString("F10"));
    Console.ReadLine();
}

Daje to następujące rezultaty:

Three Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 599999.9999886850
Decimal: 600000.0000000000

Jak widzisz, mimo że dodajemy z tego samego źródła stałą, wyniki dublowania są mniej precyzyjne (chociaż prawdopodobnie będzie zaokrąglać poprawnie), a float jest znacznie mniej precyzyjny, do tego stopnia, że został zredukowany do tylko dwóch znaczących cyfr.

 54
Author: Michael Meadows,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:33:17

Użyj dziesiętnych dla wartości bazy 10, np. obliczeń finansowych, jak sugerowali inni.

Ale double jest na ogół dokładniejsze dla dowolnych obliczonych wartości.

Na przykład, jeśli chcesz obliczyć wagę każdej linii w portfelu, użyj double, ponieważ wynik będzie prawie sumować się do 100%.

W poniższym przykładzie, dubleresult jest bliżej 1 niż decimalResult:

// Add one third + one third + one third with decimal
decimal decimalValue = 1M / 3M;
decimal decimalResult = decimalValue + decimalValue + decimalValue;
// Add one third + one third + one third with double
double doubleValue = 1D / 3D;
double doubleResult = doubleValue + doubleValue + doubleValue;

Więc jeszcze raz na przykładzie portfolio:

  • Na wartość rynkowa każdej linii w portfelu jest wartością pieniężną i prawdopodobnie najlepiej byłoby przedstawić ją jako wartość dziesiętną.

  • Waga każdej linii w portfelu (=wartość rynkowa / suma (wartość rynkowa)) jest zwykle lepiej reprezentowana jako Podwójna.

 22
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-29 20:12:25

Użyj podwójnego lub pływaka, gdy nie potrzebujesz precyzji, na przykład w grze platformowej, którą napisałem, użyłem pływaka do przechowywania prędkości gracza. Oczywiście nie potrzebuję tutaj super precyzji, ponieważ w końcu zaokrąglam do Int do rysowania na ekranie.

 6
Author: FlySwat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-29 16:42:46

W niektórych rachunkach rozważ możliwość użycia typów całkowych zamiast lub w połączeniu. Załóżmy na przykład, że zasady, na których działasz, wymagają przeniesienia każdego wyniku obliczeń z co najmniej 6 miejscami po przecinku, a wynik końcowy zostanie zaokrąglony do najbliższego grosza.

Obliczenie 1/6 z 100 $ daje 16.66666666666666666666666666666667.., więc wartość przeprowadzona w arkuszu będzie $16.666667. Zarówno podwójne, jak i dziesiętne powinny dawać ten wynik dokładnie do 6 miejsca po przecinku. Możemy jednak uniknąć kumulacyjnego błędu, przenosząc wynik do przodu jako liczbę całkowitą 16666667. Każde kolejne obliczenie może być wykonane z taką samą precyzją i przeniesione do przodu 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 roboczy) 1666667 + 1375000 = 18041667. Przesunięcie punktu dziesiętnego z powrotem daje nam 18.041667, czyli $18.04.

Chociaż ten krótki przykład nie dałby błąd skumulowany przy użyciu podwójnego lub dziesiętnego, dość łatwo jest pokazać przypadki, w których po prostu obliczenie podwójnego lub dziesiętnego i przeniesienie do przodu zgromadziłoby znaczący 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^(wymagane # miejsc po przecinku), a następnie podzielenie przez 10^(wymagane # miejsc po przecinku), aby uzyskać rzeczywistą wartość, uniknie kumulacyjnego błędu.

W sytuacjach, gdy ułamki grosze nie występują (na przykład automat), nie ma powodu, aby w ogóle używać typów nieintegralnych. Pomyśl o tym, jak o liczeniu groszy, a nie dolarów. Widziałem kod, w którym każda kalkulacja obejmowała tylko całe grosze, jednak użycie podwójnego doprowadziło do błędów! Integer only math usunął problem. Więc moja niekonwencjonalna odpowiedź jest, jeśli to możliwe, rezygnować zarówno podwójne i dziesiętne.

 4
Author: G DeMasters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-30 23:26:40

Jeśli potrzebujesz binarnego interrop z innymi językami lub platformami, może być konieczne użycie float lub double, które są standaryzowane.

 3
Author: Will Dean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-29 16:47:34

Uwaga: ten post jest oparty na informacjach o możliwościach typu dziesiętnego z http://csharpindepth.com/Articles/General/Decimal.aspx i moja własna interpretacja tego, co to znaczy. Zakładam, że Double to normalna Podwójna precyzja IEEE.

Note2: najmniejszy i największy w tym poście reffer do wielkości liczby.

Plusy "dziesiętnego".

  • "decimal" może reprezentować dokładnie liczby, które można zapisać jako (wystarczająco krótkie) ułamki dziesiętne, podwójny nie może. Jest to ważne w księgach finansowych i podobnych, gdzie ważne jest, aby wyniki dokładnie pasowały do tego, co człowiek robi obliczenia dałoby.
  • "decimal" ma znacznie większą mantissę niż"double". Oznacza to, że dla wartości w jego znormalizowanym zakresie" dziesiętny " będzie miał znacznie większą dokładność niż dwukrotnie.

Wady dziesiętne

  • będzie dużo wolniej (nie mam benchmarków, ale chyba przynajmniej rząd wielkości może więcej), dziesiętny nie będzie korzystał z akceleracji sprzętowej i arytmetyka na nim będzie wymagała stosunkowo kosztownego mnożenia/dzielenia przez potęgi 10 (co jest znacznie droższe niż mnożenie i dzielenie przez potęgi 2), aby dopasować wykładnik przed dodawaniem/odejmowaniem i przywrócić wykładnik do zakresu po mnożeniu/dzieleniu.
  • dziesiętny przepełni się wcześniej niż podwójny. dziesiętne mogą reprezentować tylko liczby do ±296-1 . Przez porównanie podwójne mogą reprezentować liczby do prawie ±21024
  • decimal będzie zaniżał wcześniej. Najmniejsze liczby reprezentowalne w układzie dziesiętnym to ±10-28 . Przez porównanie double może reprezentować wartości do 2-149 (ok. 10-45) jeśli obsługiwane są numery subnromalne i 2-126 (ok. 10-38) jeśli nie.
  • decimal zajmuje dwa razy więcej pamięci niż dwukrotnie.

Moim zdaniem powinieneś domyślnie używać "dziesiętne" dla pracy pieniędzy i innych przypadków, w których dopasowanie obliczeń człowieka dokładnie jest ważne i że należy użyć użyj podwójnego jako domyślnego wyboru przez resztę czasu.

 2
Author: plugwash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-22 16:33:15

Użyj zmiennoprzecinkowych, jeśli wartość wydajności jest wyższa od poprawności.

 0
Author: Mark Brackett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-29 16:56:59

Wybierz typ w funkcji aplikacji. Jeśli potrzebujesz precyzji jak w analizie finansowej, odpowiedziałeś na swoje pytanie. Ale jeśli aplikacja może rozliczyć się z oszacowania ok z podwójnym.

Czy Twoja aplikacja potrzebuje szybkiej kalkulacji, czy będzie miała cały czas na udzielenie odpowiedzi? To naprawdę zależy od rodzaju aplikacji.

Graficzny głodny? float lub double wystarczy. Analiza danych finansowych, meteoryt uderza w planetę precyzja ? Przydałoby się trochę precyzji :)

 0
Author: Khan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-29 17:16:30

Decimal ma szersze bajty, double jest natywnie obsługiwane przez CPU. Decimal jest bazą-10, więc konwersja dziesiętna na podwójną odbywa się podczas obliczania dziesiętnego.

For accounting - decimal
For finance - double
For heavy computation - double

Pamiętaj, że. NET CLR obsługuje tylko matematykę.Pow (double, double). Funkcja dziesiętna nie jest obsługiwana.

. NET Framework 4

[SecuritySafeCritical]
public static extern double Pow(double x, double y);
 0
Author: Jeson Martajaya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-17 21:38:30

Podwójne wartości będą serializowane do notacji naukowej domyślnie, jeśli notacja ta jest krótsza niż wyświetlacz dziesiętny. (np.00000003 będzie 3E-8) wartości dziesiętne nigdy nie będą serializowane do notacji naukowej. W przypadku serializacji do konsumpcji przez stronę zewnętrzną może to być brane pod uwagę.

 0
Author: chris klassen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-17 21:55:09

Zależy, do czego go potrzebujesz.

Ponieważ float i double są binarnymi typami danych masz niektóre diifculties i errors w sposób w liczbie rund, więc na przykład double będzie okrągły 0.1 do 0.100000001490116, double będzie również okrągły 1 / 3 do 0.33333334326441. Po prostu nie wszystkie liczby rzeczywiste mają dokładną reprezentację w typach podwójnych

Na szczęście C# obsługuje również tak zwaną dziesiętną arytmetykę zmiennoprzecinkową, gdzie liczby są reprezentowane przez dziesiętny system liczbowy, a nie system binarny. Tak więc 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.

 0
Author: Neil Meyer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-07 12:34:52