Pytanie Różnica między wartością dziesiętną, zmiennoprzecinkową i podwójną w .NET?


Jaka jest różnica pomiędzy decimal, float i double w sieci?

Kiedy ktoś skorzysta z jednego z nich?


1776
2018-03-06 11:31


pochodzenie


interesujący artykuł zetcode.com/lang/csharp/datatypes - GibboK
@Bargitta to nie prawda, nie ma float w sieci. Tylko System typ (to znaczy nie słowo kluczowe) jest wywoływany Single. - Balázs
Związane z: sandbox.mc.edu/~bennet/cs110/flt/dtof.html - Pacerier


Odpowiedzi:


float i double są Ruchomy dwójkowy rodzaje punktów. Innymi słowy, reprezentują one taką liczbę:

10001.10010110011

Liczba binarna i położenie punktu binarnego są zakodowane w wartości.

decimal jest Ruchomy dziesiętny typ punktu. Innymi słowy, reprezentują one taką liczbę:

12345.65789

Ponownie, numer i lokalizacja dziesiętny punkt są zakodowane w wartości - to właśnie sprawia, że decimal nadal typ zmiennoprzecinkowy zamiast stałego typu punktowego.

Ważne jest, aby pamiętać, że ludzie są używani do reprezentowania liczb niecałkowitych w postaci dziesiętnej i oczekują dokładnych wyników w postaci dziesiętnej; nie wszystkie liczby dziesiętne są dokładnie reprezentowane w binarnym zmiennoprzecinkowym - na przykład 0,1 - więc jeśli użyjesz binarnej wartości zmiennoprzecinkowej, otrzymasz przybliżenie do wartości 0,1. W dalszym ciągu otrzymasz przybliżenia, gdy użyjesz także zmiennoprzecinkowego przecinka dziesiętnego - wynik dzielenia 1 na 3 nie może być dokładnie odwzorowany, na przykład.

Co do tego, kiedy należy:

  • Dla wartości, które są "naturalnie dokładnymi miejscami po przecinku", dobrze jest użyć decimal. Jest to zwykle odpowiednie dla wszelkich koncepcji wymyślonych przez ludzi: wartości finansowe są najbardziej oczywistym przykładem, ale są też inne. Zastanów się na przykład dla nurków lub łyżwiarzy.

  • Dla wartości, które są bardziej artefaktami natury, których naprawdę nie można zmierzyć dokładnie tak czy inaczej, float/double są bardziej odpowiednie. Na przykład dane naukowe byłyby zwykle reprezentowane w tej formie. W tym przypadku początkowe wartości nie będą "dziesiętnie dokładne", więc nie jest ważne, aby oczekiwane wyniki zachowały "dokładność dziesiętną". Zmienne typy punktów binarnych są o wiele szybsze niż dziesiętne.


1969
2018-03-06 11:56



float/double zazwyczaj nie reprezentują liczb jako 101.101110, zwykle jest reprezentowany jako coś w stylu 1101010 * 2^(01010010) - wykładnik - Mingwei Samuel
@Hazzard: To właśnie oznacza część odpowiedzi "i lokalizacja punktu binarnego". - Jon Skeet
Jestem zaskoczony, że nie zostało to już powiedziane, float to słowo kluczowe alias C # i nie jest typem .Net. jego System.Single.. single i double są pływającymi typami punktów binarnych. - Brett Caswell
czekaj .... nie jest dziesiętny reprezentowany w 1s i 0s ostatecznie? Myślałem, że komputery mogą działać tylko w formie binarnej. więc dziesiętny jest ostatecznie typem binarnym, nieprawdaż? - BKSpurgeon
@BKSpurgeon: Cóż, tylko w ten sam sposób, w jaki możesz to powiedzieć wszystko jest typem binarnym, w którym to momencie staje się dość bezużyteczną definicją. Dziesiętny jest typem dziesiętnym, ponieważ jest to liczba reprezentowana jako liczba całkowita i skala, tak, że wynik jest znaczący i * 10 ^ skala, podczas gdy zmienna i podwójna są znaczące i skala * 2 ^. Wpisujesz liczbę dziesiętną i przenosisz przecinek dziesiętny na tyle, na prawo, że masz liczbę całkowitą do obliczenia znaczenia i skali. Dla float / double zaczniesz od liczby zapisanej w postaci binarnej. - Jon Skeet


Precyzja to główna różnica.

Pływak - 7 cyfr (32-bitowe)

Podwójnie-15-16 cyfr (64-bitowych)

Dziesiętny -28-29 znaczących cyfr (128 bitów)

Miejsca dziesiętne mają znacznie większą precyzję i są zwykle używane w aplikacjach finansowych, które wymagają dużej dokładności. Dziesiętne są znacznie wolniejsze (do 20 razy w niektórych testach) niż podwójne / zmiennoprzecinkowe.

Ułamki dziesiętne i spływy / dwójki nie mogą być porównywane bez rzutowania, podczas gdy spływy i dwójki mogą. Znaki dziesiętne pozwalają także na kodowanie lub kończenie zer.

float flt = 1F/3;
double dbl = 1D/3;
decimal dcm = 1M/3;
Console.WriteLine("float: {0} double: {1} decimal: {2}", flt, dbl, dcm);

Wynik:

float: 0.3333333  
double: 0.333333333333333  
decimal: 0.3333333333333333333333333333

896
2018-03-06 11:33



@ Thecrocodilehunter: przepraszam, ale nie. Dziesiętny może reprezentować wszystkie liczby, które mogą być reprezentowane w notacji dziesiętnej, ale nie na przykład 1/3. 1,0m / 3,0m oszacuje na 0,33333333 ... z dużą, ale skończoną liczbą 3s na końcu. Pomnożenie przez 3 nie zwróci dokładnego 1,0. - Erik P.
@ Thecrocodilehunter: Myślę, że mylicie dokładność i precyzję. W tym kontekście są różne rzeczy. Precyzja to liczba cyfr dostępnych do przedstawienia liczby. Im większa precyzja, tym mniej trzeba zaokrąglić. Żaden typ danych nie ma nieskończonej precyzji. - Igby Largeman
@ Thecrocodilehunter: Zakładasz, że mierzona wartość jest dokładnie  0.1 - to rzadko ma miejsce w realnym świecie! Każdy skończony format przechowywania zmodyfikuje nieskończoną liczbę możliwych wartości do skończonej liczby wzorów bitowych. Na przykład, float się połączy 0.1 i 0.1 + 1e-8, podczas decimal się połączy 0.1 i 0.1 + 1e-29. Pewnie, w podanym zakresieniektóre wartości mogą być reprezentowane w dowolnym formacie bez utraty dokładności (np. float może przechowywać dowolną liczbę całkowitą do 1,6 e7 bez utraty dokładności) - ale to nadal nie jest możliwe nieskończony precyzja. - Daniel Pryden
@ Thecrocodilehunter: Opuściłeś mój punkt widzenia. 0.1 jest nie jest to wartość specjalna! Jedyne, co sprawia 0.1 "lepszy niż 0.10000001 jest ponieważ istoty ludzkie jak podstawa 10. I nawet z float wartość, jeśli zainicjalizujesz dwie wartości za pomocą 0.1 w ten sam sposób, obie będą miały tę samą wartość. Po prostu ta wartość nie będzie dokładnie  0.1 -- To będzie najbliższa wartość 0.1 które można dokładnie przedstawić jako float. Jasne, z binarnymi spławikami, (1.0 / 10) * 10 != 1.0, ale z wartościami dziesiętnymi, (1.0 / 3) * 3 != 1.0 zarówno. Ani jest doskonale precyzyjny. - Daniel Pryden
@ Thecrocodilehunter: Nadal nie rozumiesz. Nie wiem, jak to powiedzieć bardziej wyraźnie: w C, jeśli tak double a = 0.1; double b = 0.1; następnie a == b  będzie prawdą. Po prostu to a i b będzie obie niezupełnie równy 0.1. W języku C #, jeśli tak decimal a = 1.0m / 3.0m; decimal b = 1.0m / 3.0m; następnie a == b będzie również prawdą. Ale w takim przypadku ani z a ani b będzie dokładnie równy 1/3 - będą równe 0.3333.... W obie W niektórych przypadkach utracono dokładność z powodu reprezentacji. Upierasz się tak decimal ma "nieskończoną" precyzję, która jest fałszywy. - Daniel Pryden


Struktura dziesiętna jest ściśle ukierunkowana na obliczenia finansowe wymagające dokładności, które są stosunkowo nietolerancyjne dla zaokrąglania. Miejsca dziesiętne nie są jednak wystarczające dla aplikacji naukowych z kilku powodów:

  • Pewna utrata precyzji jest dopuszczalna w wielu obliczeniach naukowych ze względu na praktyczne granice problemu fizycznego lub mierzonego artefaktu. Utrata precyzji jest niedopuszczalna w finansach.
  • Wartość dziesiętna jest dużo (dużo) wolniejsza od wartości zmiennoprzecinkowej i podwójna dla większości operacji, głównie dlatego, że operacje zmiennoprzecinkowe są wykonywane w trybie binarnym, podczas gdy operacje dziesiętne wykonywane są w bazie 10 (tj. Zmienne i podwójne są obsługiwane przez sprzęt FPU, taki jak MMX / SSE , podczas gdy wartości dziesiętne są obliczane w oprogramowaniu).
  • Dziesiętny ma niedopuszczalnie mniejszy zakres wartości niż podwójny, mimo że obsługuje więcej cyfr dokładności. Dlatego Decimal nie może być użyty do reprezentowania wielu wartości naukowych.

66
2018-04-13 13:55



Jeśli wykonujesz obliczenia finansowe, musisz bezwzględnie przetasować własne typy danych lub znaleźć dobrą bibliotekę, która pasuje do Twoich konkretnych potrzeb. Dokładność w kontekście finansowym jest określona przez (ludzkie) organy normalizacyjne i mają one bardzo specyficzne zlokalizowane (zarówno pod względem czasu, jak i geografii) zasady dotyczące wykonywania obliczeń. Rzeczy takie jak poprawne zaokrąglenie nie są przechwytywane w prostych typach liczbowych w .Net. Umiejętność wykonywania obliczeń jest tylko niewielką częścią układanki. - James Moore


enter image description here

aby uzyskać więcej informacji, możesz przejść do źródła tego zdjęcia:

http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/921a8ffc-9829-4145-bdc9-a96c1ec174a5


54
2018-06-07 12:50



-1 dla twierdzenia, że ​​dziesiętny wynosi 96 bitów. MSDN wyraźnie mówi 128. - Ben Voigt
A jeśli kopiesz głębiej, to używasz 102 bitów, 128 przechowywanych. Nie ma możliwości dopasowania go w 12 bajtach. - Ben Voigt
Pominięto największą różnicę, która jest podstawą używaną dla typu dziesiętnego (dziesiętna jest przechowywana jako podstawa 10, wszystkie inne wymienione typy liczb są podstawą 2). - BrainSlugs83
Zakresy wartości dla opcji Pojedyncze i Podwójne nie są poprawnie przedstawione na powyższym obrazku lub w postach na forum źródłowym. Ponieważ nie możemy w prosty sposób opisać tego tekstu tutaj, użyj znaku karetki: Single powinno być 10 ^ -45 i 10 ^ 38, a Double powinno być 10 ^ -324 i 10 ^ 308. Ponadto MSDN ma zmiennoprzecinkowe z zakresu od -3,4x10 ^ 38 do + 3,4x10 ^ 38. Wyszukaj MSDN dla System.Single i System.Double w przypadku zmian linków. Pojedynczy: msdn.microsoft.com/en-us/library/b1e65aza.aspx Podwójnie: msdn.microsoft.com/en-us/library/678hzkk9.aspx - deegee


float 7 cyfr precyzji

double ma około 15 cyfr precyzji

decimal ma około 28 cyfr precyzji

Jeśli potrzebujesz lepszej dokładności, użyj double zamiast float. W nowoczesnych procesorach oba typy danych mają niemal taką samą wydajność. Jedyną korzyścią płynącą z używania pływaka jest to, że zajmują mniej miejsca. Praktycznie ma to znaczenie tylko wtedy, gdy masz ich wielu.

Znalazłem to interesujące. Co każdy informatyk powinien wiedzieć o arytmetyki zmiennoprzecinkowej


40
2017-08-29 00:06



@RogerLipscombe: Rozważałbym double właściwe w zastosowaniach księgowych w tych przypadkach (i w zasadzie tylko w tych przypadkach), w których nie był dostępny typ całkowity większy niż 32-bitowy, a double był używany tak, jakby był 53-bitowym typem całkowitym (np. do trzymania całej liczby pensów lub całej liczby setnych centów). W dzisiejszych czasach nie jest to zbyt przydatne, ale wiele języków zyskało możliwość używania wartości zmiennoprzecinkowych o podwójnej precyzji na długo przed tym, zanim uzyskały 64-bitową (lub w niektórych przypadkach nawet 32-bitową!) Liczbę całkowitą. - supercat
Twoja odpowiedź sugeruje, że precyzja jest jedyną różnicą między tymi typami danych. Biorąc pod uwagę arytmetykę zmiennoprzecinkową binarną jest zwykle realizowane w sprzętowa FPUwydajność to znacząca różnica. Może to być nieistotne dla niektórych aplikacji, ale ma krytyczne znaczenie dla innych. - saille
@supercat double jest nigdy właściwe w aplikacjach księgowych. Ponieważ Double może jedynie przybliżać wartości dziesiętne (nawet w zakresie własnej precyzji). Dzieje się tak, ponieważ double przechowuje wartości w formacie base-2 (binary) -centric. - BrainSlugs83
@ BrainSlugs83: Przechowywanie w typach zmiennoprzecinkowych niecałkowita liczba Ilości byłyby niewłaściwe, ale historycznie bardzo często języki miały typy zmiennoprzecinkowe, które mogły precyzyjnie reprezentować większą liczbę całkowitą, niż mogłyby reprezentować ich typy całkowite. Być może najbardziej ekstremalnym przykładem był model Turbo-87, którego jedyne typy liczb całkowitych były ograniczone do -32768 do +32767, ale których Real czy IIRC może reprezentować wartości do 1,8E + 19 z dokładnością jednostki. Sądzę, że użycie aplikacji do księgowania byłoby znacznie bardziej uciążliwe Real do reprezentowania całej liczby pensów niż ... - supercat
... aby spróbował wykonać matematykę z wieloma precyzjami, korzystając z 16-bitowych wartości. W przypadku większości innych języków różnica nie była tak skrajna, ale od dawna bardzo często języki nie mają żadnego typu całkowitego, który wykracza poza 4E9, ale ma double typ, który miał dokładność jednostki do 9E15. Jeśli musisz przechowywać liczby całkowite, które są większe niż największy dostępny typ liczby całkowitej, użyj doublemoże być prostsze i bardziej wydajne niż próby korygowania matematyki wielopunktowej, szczególnie biorąc pod uwagę, że podczas gdy procesorzy mają instrukcje wykonywania 16x16-> 32 lub ... - supercat


Nikt o tym nie wspomniał

W domyślnych ustawieniach, Floats (System.Single) i Double (System.Double) nigdy nie użyją   sprawdzanie przepełnienia, a funkcja dziesiętna (System.Decimal) zawsze będzie używana   sprawdzanie przepełnienia.

mam na myśli

decimal myNumber = decimal.MaxValue;
myNumber += 1;

rzuca Przepełnienie wyjątku.

Ale te nie:

float myNumber = float.MaxValue;
myNumber += 1;

&

double myNumber = double.MaxValue;
myNumber += 1;

29
2018-01-02 13:12



float.MaxValue+1 == float.MaxValue, tak jak decimal.MaxValue+0.1D == decimal.MaxValue. Być może miałeś na myśli coś takiego float.MaxValue*2? - supercat
@supercar Ale nie jest prawdą, że decimal.MaxValue + 1 == decimal.MaxValue - GorkemHalulu
@supercar decimal.MaxValue + 0.1m == decimal.MaxValue ok - GorkemHalulu
The System.Decimal zgłasza wyjątek, zanim stanie się niezdolny do rozróżnienia całych jednostek, ale jeśli aplikacja ma się zajmować np. dolarów i centów, może być za późno. - supercat


  1. Double i float mogą być dzielone przez integer zero bez wyjątku zarówno podczas kompilacji, jak i czasu wykonywania.
  2. Dziesiętny nie może być dzielony przez liczbę całkowitą zero. Kompilacja zawsze zawiedzie, jeśli to zrobisz.

25
2017-07-29 07:21



Z pewnością mogą! Mają też kilka "magicznych" wartości, takich jak Nieskończoność, Negatywna Nieskończoność i NaN (nie liczba), które czynią go bardzo użytecznym do wykrywania pionowych linii podczas obliczania nachylenia ... Dalej, jeśli musisz zdecydować między wywołaniem pływaka .TryParse, double.TryParse i decimal.TryParse (aby sprawdzić, czy łańcuch jest liczbą, na przykład), polecam użycie double lub float, ponieważ będą one parsować odpowiednio "Infinity", "-Infinity" i "NaN" , natomiast dziesiętny nie. - BrainSlugs83
Kompilacja nie powiedzie się, jeśli spróbujesz podzielić literał decimal o zero (CS0020), a to samo dotyczy całkowych literałów. Jednak jeśli wartość dziesiętna środowiska wykonawczego zostanie podzielona przez zero, otrzymasz wyjątek, a nie błąd kompilacji. - Drew Noakes
@ BrainSlugs83 Jednak możesz nie chcieć parsować "Infinity" lub "NaN" w zależności od kontekstu. Wydaje się, że jest to dobry exploit do wprowadzania danych przez użytkownika, jeśli programista nie jest wystarczająco rygorystyczny. - Winter


Nie będę powtarzał tonów dobrych (i złych) informacji, na które już udzielono odpowiedzi w innych odpowiedziach i komentarzach, ale odpowiem na twoje pytanie uzupełniające poradą:

Kiedy ktoś skorzysta z jednego z nich?

Użyj dziesiętnych dla policzone wartości

Użyj float / double dla wymierzony wartości

Kilka przykładów:

  • pieniądze (czy liczymy pieniądze czy mierzymy pieniądze?)

  • odległość (liczymy odległość lub odległość pomiaru? *)

  • wyniki (liczymy wyniki lub wyniki pomiarów?)

Zawsze liczymy pieniądze i nie powinniśmy ich mierzyć. Zwykle mierzymy odległość. Często liczymy wyniki.

* W niektórych przypadkach, co bym nazwał nominalna odległość, możemy rzeczywiście chcieć "policzyć" dystans. Na przykład, być może mamy do czynienia z krajowymi znakami pokazującymi odległości do miast i wiemy, że te odległości nigdy nie mają więcej niż jedną cyfrę dziesiętną (xxx.x km).


25
2018-04-22 15:18