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


Odpowiedzi:


2265

floati doublezmiennoprzecinkowymi typami binarnymi . Innymi słowy, reprezentują liczbę taką jak ta:

10001.10010110011

Zarówno liczba binarna, jak i lokalizacja punktu binarnego są zakodowane w ramach wartości.

decimaljest zmiennoprzecinkowym typem dziesiętnym . Innymi słowy, reprezentują liczbę taką jak ta:

12345.65789

Ponownie liczba i położenie przecinka dziesiętnego są zakodowane w ramach wartości - to sprawia, że decimalnadal jest to zmiennoprzecinkowy typ zamiast stałego.

Należy zauważyć, że ludzie są przyzwyczajeni do przedstawiania liczb całkowitych w postaci dziesiętnej i oczekują dokładnych wyników w postaci dziesiętnej; nie wszystkie liczby dziesiętne są dokładnie reprezentowalne w binarnym zmiennoprzecinkowym - na przykład 0,1 - więc jeśli użyjesz binarnej wartości zmiennoprzecinkowej, faktycznie uzyskasz przybliżenie do 0,1. Nadal będziesz otrzymywać przybliżenia, używając również zmiennoprzecinkowego miejsca dziesiętnego - na przykład wyniku dzielenia 1 przez 3 nie można dokładnie przedstawić.

Co do zastosowania, gdy:

  • W przypadku wartości, które są „naturalnie dokładnymi miejscami dziesiętnymi”, 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. Weźmy na przykład punktację dla nurków lub łyżwiarzy.

  • Dla wartości, które są więcej artefaktami natury, których tak naprawdę nie można dokładnie zmierzyć , float/ doublesą bardziej odpowiednie. Na przykład dane naukowe byłyby zwykle reprezentowane w tej formie. Tutaj pierwotne wartości nie będą na początku „dziesiętnie dokładne”, więc nie jest ważne, aby oczekiwane wyniki zachowały „dokładność dziesiętną”. Zmienne zmiennoprzecinkowe są znacznie szybsze w pracy niż dziesiętne.


58
float/ doublezwykle nie reprezentują liczb, ponieważ 101.101110zwykle jest przedstawiany jako coś w 1101010 * 2^(01010010)stylu wykładnika
Mingwei Samuel

79
@Hazzard: To właśnie oznacza część „i położenie punktu binarnego” w odpowiedzi.
Jon Skeet,

112
Dziwię się, że nie zostało to jeszcze powiedziane, floatjest słowem kluczowym aliasu C # i nie jest typem .Net. to System.Single.. singlei doublesą zmiennoprzecinkowe typy binarne.
Brett Caswell

54
@BKSpurgeon: Cóż, tylko w ten sam sposób, w jaki można powiedzieć, że wszystko jest typem binarnym, w którym to momencie staje się dość bezużyteczną definicją. Dziesiętny jest typem dziesiętnym, ponieważ jest liczbą reprezentowaną jako liczba całkowita i skala, tak że wynikiem jest skala znaczeń i * 10 ^, podczas gdy liczba zmiennoprzecinkowa i podwójna są skalami znaczącymi i * 2 ^. Bierzesz liczbę zapisaną dziesiętnie i przesuwasz przecinek dziesiętny wystarczająco daleko w prawo, aby uzyskać liczbę całkowitą, aby obliczyć znaczenie i skalę. W przypadku liczb zmiennoprzecinkowych zaczynasz od liczby zapisanej w formacie binarnym.
Jon Skeet

21
Kolejna różnica: float 32-bit; podwójny 64-bit; i dziesiętny 128-bitowy.
David

1072

Precyzja jest główną różnicą.

Liczba zmiennoprzecinkowa - 7 cyfr (32 bity)

Podwójne -15-16 cyfr (64 bity)

Liczba dziesiętna -28-29 cyfr znaczących (128 bitów)

Ułamki dziesiętne mają znacznie wyższą precyzję i są zwykle używane w aplikacjach finansowych, które wymagają wysokiego stopnia dokładności. Dziesiętne są znacznie wolniejsze (w niektórych testach nawet 20 razy) niż podwójne / zmiennoprzecinkowe.

Liczby dziesiętne i zmiennoprzecinkowe / podwajanie nie mogą być porównywane bez obsady, podczas gdy zmiennoprzecinkowe i podwajające mogą. Dziesiętne pozwalają również na kodowanie lub końcowe zera.

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

65
@Thecrocodilehunter: przepraszam, ale nie. Dziesiętna może reprezentować wszystkie liczby, które mogą być reprezentowane w notacji dziesiętnej, ale na przykład nie 1/3. 1,0 m / 3,0 m wyniesie 0,333333333 ... z dużą, ale skończoną liczbą 3 s na końcu. Pomnożenie go przez 3 nie zwróci dokładnej wartości 1,0.
Erik P.,

50
@Thecrocodilehunter: Myślę, że mylisz dokładność i precyzję. W tym kontekście są to różne rzeczy. Precyzja to liczba cyfr dostępnych do przedstawienia liczby. Im większa precyzja, tym mniej musisz zaokrąglać. Żaden typ danych nie ma nieskończonej precyzji.
Igby Largeman

13
@Thecrocodilehunter: Zakładasz, że mierzona wartość jest dokładnie 0.1 taka - rzadko w prawdziwym świecie! Każdy skończony format przechowywania połączy nieskończoną liczbę możliwych wartości ze skończoną liczbą wzorów bitowych. Na przykład floatbędzie się łączyć 0.1i 0.1 + 1e-8, podczas gdy decimalbędzie się łączyć 0.1i 0.1 + 1e-29. Oczywiście, w danym zakresie pewne wartości mogą być reprezentowane w dowolnym formacie z zerową utratą dokładności (np. floatMogą przechowywać dowolną liczbę całkowitą do 1.6e7 z zerową utratą dokładności) - ale to wciąż nie jest nieskończona dokładność.
Daniel Pryden,

27
@Thecrocodilehunter: Przegapiłeś mój punkt. 0.1to nie jest specjalna wartość ! Jedyne, co czyni 0.1„lepszym” niż 0.10000001to, że ludzie tacy jak podstawa 10. I nawet z floatwartością, jeśli zainicjujesz dwie wartości w 0.1ten sam sposób, obie będą miały tę samą wartość . Tyle, że ta wartość nie będzie dokładnie 0.1 - będzie najbliższą wartością, 0.1którą można dokładnie przedstawić jakofloat . Jasne, z liczbami binarnymi (1.0 / 10) * 10 != 1.0, ale z liczbami dziesiętnymi (1.0 / 3) * 3 != 1.0. Żadne z nich nie jest idealnie precyzyjne.
Daniel Pryden,

16
@Thecrocodilehunter: Nadal nie rozumiesz. Nie wiem, jak powiedzieć to bardziej wyraźnie: w C, jeśli tak, double a = 0.1; double b = 0.1;to a == b będzie prawdą . Po prostu to ai oba nie bbędą dokładnie równe . W języku C #, jeśli to zrobisz, to również będzie prawdą. Ale w takim przypadku żaden z nich nie będzie dokładnie równy - oba będą równe . W obu przypadkach utrata dokładności wynika z reprezentacji. Uparcie twierdzisz, że ma „nieskończoną” precyzję, co jest fałszem . 0.1decimal a = 1.0m / 3.0m; decimal b = 1.0m / 3.0m;a == bab1/30.3333...decimal
Daniel Pryden

84

Struktura dziesiętna jest ściśle dostosowana do obliczeń finansowych wymagających dokładności, które są stosunkowo nietolerancyjne zaokrąglania. Ułamki dziesiętne nie są odpowiednie do zastosowań naukowych, jednak z kilku powodów:

  • Pewna utrata precyzji jest dopuszczalna w wielu obliczeniach naukowych ze względu na praktyczne ograniczenia mierzonego problemu fizycznego lub artefaktu. Utrata precyzji jest nie do zaakceptowania w finansach.
  • Dziesiętny jest znacznie (dużo) wolniejszy niż zmiennoprzecinkowy i podwójny w większości operacji, głównie dlatego, że operacje zmiennoprzecinkowe są wykonywane w systemie binarnym, podczas gdy operacje dziesiętne są wykonywane w bazie 10 (tzn. Zmiennoprzecinkowe i podwójne są obsługiwane przez sprzęt FPU, taki jak MMX / SSE , natomiast dziesiętne są obliczane w oprogramowaniu).
  • Dziesiętny ma niedopuszczalnie mniejszy zakres wartości niż podwójny, mimo że obsługuje więcej cyfr precyzji. Dlatego Decimal nie może być używany do reprezentowania wielu wartości naukowych.

5
Jeśli wykonujesz obliczenia finansowe, absolutnie musisz przetasować własne typy danych lub znaleźć dobrą bibliotekę, która dokładnie odpowiada twoim potrzebom. Dokładność w otoczeniu finansowym jest określana przez (ludzkie) organy normalizacyjne i mają one bardzo specyficzne, zlokalizowane (zarówno w czasie, jak i geograficznym), zasady dotyczące wykonywania obliczeń. Rzeczy takie jak prawidłowe zaokrąglanie nie są rejestrowane w prostych numerycznych typach danych w .Net. Umiejętność wykonywania obliczeń jest tylko bardzo małą częścią układanki.
James Moore,

76
+---------+----------------+---------+----------+---------------------------------------------+
| C#      | .Net Framework | Signed? | Bytes    | Possible Values                             |
| Type    | (System) type  |         | Occupied |                                             |
+---------+----------------+---------+----------+---------------------------------------------+
| sbyte   | System.Sbyte   | Yes     | 1        | -128 to 127                                 |
| short   | System.Int16   | Yes     | 2        | -32768 to 32767                             |
| int     | System.Int32   | Yes     | 4        | -2147483648 to 2147483647                   |
| long    | System.Int64   | Yes     | 8        | -9223372036854775808 to 9223372036854775807 |
| byte    | System.Byte    | No      | 1        | 0 to 255                                    |
| ushort  | System.Uint16  | No      | 2        | 0 to 65535                                  |
| uint    | System.UInt32  | No      | 4        | 0 to 4294967295                             |
| ulong   | System.Uint64  | No      | 8        | 0 to 18446744073709551615                   |
| float   | System.Single  | Yes     | 4        | Approximately ±1.5 x 10-45 to ±3.4 x 1038   |
|         |                |         |          |  with 7 significant figures                 |
| double  | System.Double  | Yes     | 8        | Approximately ±5.0 x 10-324 to ±1.7 x 10308 |
|         |                |         |          |  with 15 or 16 significant figures          |
| decimal | System.Decimal | Yes     | 12       | Approximately ±1.0 x 10-28 to ±7.9 x 1028   |
|         |                |         |          |  with 28 or 29 significant figures          |
| char    | System.Char    | N/A     | 2        | Any Unicode character (16 bit)              |
| bool    | System.Boolean | N/A     | 1 / 2    | true or false                               |
+---------+----------------+---------+----------+---------------------------------------------+

Zobacz tutaj, aby uzyskać więcej informacji .


5
Pominąłeś największą różnicę, jaką jest podstawa używana dla typu dziesiętnego (dziesiętny jest przechowywany jako podstawa 10, wszystkie inne wymienione typy liczbowe to podstawa 2).
BrainSlugs83

1
Zakresy wartości dla pojedynczego i podwójnego nie są poprawnie przedstawione na powyższym obrazku lub w źródłowym poście na forum. Ponieważ nie możemy łatwo nadpisać tekstu tutaj, użyj znaku karetki: Single powinien mieć 10 ^ -45 i 10 ^ 38, a Double powinien mieć 10 ^ -324 i 10 ^ 308. Ponadto MSDN ma liczbę zmiennoprzecinkową o zakresie od -3,4x10 ^ 38 do + 3,4x10 ^ 38. Wyszukaj MSDN dla System.Single i System.Double w przypadku zmian linków. Single: msdn.microsoft.com/en-us/library/b1e65aza.aspx Double: msdn.microsoft.com/en-us/library/678hzkk9.aspx
deegee

2
Dziesiętny to 128 bitów ... oznacza, że ​​zajmuje 16 bajtów, a nie 12
1477332

51

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

Kiedy ktoś użyłby jednego z nich?

Użyj wartości dziesiętnych dla zliczonych wartości

Użyj wartości zmiennoprzecinkowych / podwójnych dla zmierzonych wartości

Kilka przykładów:

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

  • odległość (czy liczymy odległość czy mierzymy odległość? *)

  • wyniki (czy liczymy wyniki czy mierzymy wyniki?)

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

* W niektórych przypadkach, co nazwałbym odległością nominalną , rzeczywiście możemy chcieć „policzyć” odległość. Może na przykład mamy do czynienia z znakami państwowymi, które pokazują odległości do miast i wiemy, że odległości te nie mają więcej niż jednej cyfry dziesiętnej (xxx.x km).


1
Naprawdę podoba mi się ta odpowiedź, zwłaszcza pytanie „czy liczymy czy mierzymy pieniądze?” Jednak poza pieniędzmi nie mogę wymyślić niczego, co jest „policzone”, co nie jest po prostu liczbą całkowitą. Widziałem niektóre aplikacje, które używają dziesiętnych po prostu dlatego, że double ma zbyt mało cyfr znaczących. Innymi słowy, można użyć dziesiętnej, ponieważ C # nie ma poczwórnego typu en.wikipedia.org/wiki/Quadruple-precision_floating-point_format
John Henckel

48

float 7 cyfr precyzji

double ma około 15 cyfr precyzji

decimal ma około 28 cyfr precyzji

Jeśli potrzebujesz większej dokładności, użyj podwójnej zamiast pływakowej. We współczesnych procesorach oba typy danych mają prawie taką samą wydajność. Jedyną zaletą używania pływaka jest to, że zajmują mniej miejsca. Praktycznie ma znaczenie tylko wtedy, gdy masz ich wiele.

Uważam, że to interesujące. Co każdy informatyk powinien wiedzieć o arytmetyki zmiennoprzecinkowej


1
@RogerLipscombe: Rozważyłbym doublewłaściwe zastosowanie w aplikacjach księgowych w tych przypadkach (i zasadniczo tylko w tych przypadkach), w których nie był dostępny typ liczby całkowitej większy niż 32 bity i doublebył używany tak, jakby był to 53-bitowy typ liczby całkowitej (np. Do przechowywania cała liczba centów lub cała setna setna centa). Obecnie niewiele takich zastosowań, ale wiele języków zyskało możliwość korzystania z wartości zmiennoprzecinkowych o podwójnej precyzji na długo przed uzyskaniem 64-bitowej (lub w niektórych przypadkach nawet 32-bitowej!) Matematyki całkowitej.
supercat

1
Twoja odpowiedź sugeruje, że precyzja jest jedyną różnicą między tymi typami danych. Biorąc pod uwagę, że binarna arytmetyka zmiennoprzecinkowa jest zwykle implementowana w sprzętowym FPU , wydajność jest znaczącą różnicą. Może to być nieistotne w przypadku niektórych aplikacji, ale ma kluczowe znaczenie dla innych.
żaglówka

6
@supercat double nigdy nie jest właściwy w aplikacjach księgowych. Ponieważ Double może jedynie przybliżać wartości dziesiętne (nawet w zakresie własnej precyzji). Wynika to z faktu, że double przechowuje wartości w formacie koncentrycznym 2 (binarnym).
BrainSlugs83

2
@ BrainSlugs83: Użycie typów zmiennoprzecinkowych do przechowywania liczb niecałkowitych byłoby niewłaściwe, ale w przeszłości bardzo często języki posiadały typy zmiennoprzecinkowe, które mogłyby precyzyjnie przedstawiać większe liczby całkowite niż ich typy całkowite . Być może najbardziej ekstremalnym przykładem był Turbo-87, którego jedyne typy całkowite były ograniczone od -32768 do +32767, ale których RealIIRC mógł reprezentować wartości do 1.8E + 19 z dokładnością do jednostki. Myślę, że byłoby znacznie rozsądniej, gdyby aplikacja księgowa Realreprezentowała całą liczbę groszy niż ...
supercat

1
... aby spróbować wykonać matematykę z dużą precyzją przy użyciu szeregu 16-bitowych wartości. W przypadku większości innych języków różnica nie była tak ekstremalna, ale przez długi czas bardzo często zdarzało się, że języki nie miały żadnych liczb całkowitych wykraczających poza 4E9, ale takich, doublektóre miały dokładność jednostkową do 9E15. Jeśli trzeba przechowywać liczby całkowite, które są większe niż największy dostępny typ liczb całkowitych, użycie doublemoże być prostsze i bardziej wydajne niż próba fałszowania matematyki wieloprecyzyjnej, szczególnie biorąc pod uwagę, że podczas gdy procesory mają instrukcje wykonywania 16x16-> 32 lub. ..
supercat,

36

Nikt o tym nie wspominał

W ustawieniach domyślnych zmiennoprzecinkowe (System.Single) i doubles (System.Double) nigdy nie będą używać sprawdzania przepełnienia, podczas gdy Decimal (System.Decimal) zawsze będzie używać sprawdzania przepełnienia.

mam na myśli

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

zgłasza wyjątek OverflowException .

Ale te nie:

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

I

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

1
float.MaxValue+1 == float.MaxValue, podobnie jak decimal.MaxValue+0.1D == decimal.MaxValue. Może miałeś na myśli coś takiego float.MaxValue*2?
supercat

@supercar Ale to nieprawda, że ​​decimal.MaxValue + 1 == decimal.MaxValue
GorkemHalulu

@supercar decimal.MaxValue + 0.1m == decimal.MaxValue ok
GorkemHalulu 14.01.2015

1
System.DecimalZgłasza wyjątek tuż przed staje się niezdolny do odróżnienia całych jednostek, ale jeśli aplikacja ma do czynienia z np dolarów i centów, że może być za późno.
supercat

28
  1. Podwójne i zmiennoprzecinkowe można podzielić przez liczbę całkowitą zero bez wyjątku zarówno w czasie kompilacji, jak i wykonywania.
  2. Dziesiętnej nie można podzielić przez liczbę całkowitą zero. Jeśli to zrobisz, kompilacja zawsze zawiedzie.

6
Z pewnością mogą! Mają także kilka „magicznych” wartości, takich jak Nieskończoność, Negatywna nieskończoność i NaN (nie liczba), co sprawia, że ​​jest bardzo przydatny do wykrywania linii pionowych podczas obliczania nachyleń ... Co więcej, jeśli musisz zdecydować między wywołaniem liczby zmiennoprzecinkowej .TryParse, double.TryParse i decimal.TryParse (na przykład, aby wykryć, czy ciąg znaków jest liczbą), zalecam użycie double lub float, ponieważ będą one poprawnie analizować „Infinity”, „-Infinity” i „NaN” , podczas gdy dziesiętny nie.
BrainSlugs83

Kompilacja kończy się niepowodzeniem, jeśli spróbujesz podzielić literał decimalprzez zero (CS0020), i to samo dotyczy literałów całkowitych. Jeśli jednak wartość dziesiętna środowiska wykonawczego zostanie podzielona przez zero, otrzymasz wyjątek, a nie błąd kompilacji.
Drew Noakes,

@ BrainSlugs83 Jednak w zależności od kontekstu możesz nie chcieć analizować „Infinity” lub „NaN”. Wydaje się, że jest to dobry exploit do wprowadzania danych przez użytkownika, jeśli programista nie jest wystarczająco rygorystyczny.
Zima

28

Jak wspomniano, liczby całkowite są liczbami całkowitymi. Nie mogą zapamiętać czegoś takiego, jak .7, .42 i .007. Jeśli chcesz przechowywać liczby, które nie są liczbami całkowitymi, potrzebujesz innego rodzaju zmiennej. Możesz użyć typu podwójnego lub typu zmiennoprzecinkowego. Ustawiasz te typy zmiennych dokładnie w ten sam sposób: zamiast używać słowa int, wpisujesz doublelub float. Lubię to:

float myFloat;
double myDouble;

( floatto skrót od „zmiennoprzecinkowy” i oznacza po prostu liczbę z punktem na końcu czegoś).

Różnica między nimi polega na wielkości liczb, które mogą pomieścić. W floattwoim numerze możesz mieć do 7 cyfr. Dla doubles możesz mieć do 16 cyfr. Aby być bardziej precyzyjnym, oto oficjalny rozmiar:

float:  1.5 × 10^-45  to 3.4 × 10^38  
double: 5.0 × 10^-324 to 1.7 × 10^308

floatjest liczbą 32-bitową i doublejest liczbą 64-bitową.

Kliknij dwukrotnie nowy przycisk, aby uzyskać kod. Dodaj następujące trzy wiersze do kodu przycisku:

double myDouble;
myDouble = 0.007;
MessageBox.Show(myDouble.ToString());

Zatrzymaj swój program i wróć do okna kodowania. Zmień tę linię:

myDouble = 0.007;
myDouble = 12345678.1234567;

Uruchom program i kliknij podwójny przycisk. W oknie komunikatu poprawnie wyświetlany jest numer. Dodaj kolejną liczbę na końcu, a C # ponownie zaokrągli w górę lub w dół. Morał jest taki, że jeśli chcesz dokładności, uważaj na zaokrąglanie!


2
„Wskazać coś”, o którym wspominałeś, jest ogólnie nazywane „ułamkową częścią” liczby. „Zmiennoprzecinkowy” nie oznacza „liczby z punktem coś na końcu”; ale zamiast tego „zmiennoprzecinkowy” rozróżnia rodzaj liczby, w przeciwieństwie do liczby „stałego punktu” (która może również przechowywać wartość ułamkową); różnica polega na tym, czy precyzja jest stała, czy zmienna. - Liczby zmiennoprzecinkowe dają znacznie większy zakres dynamiki wartości (Min i Max), kosztem precyzji, podczas gdy liczby stacjonarne zapewniają stałą precyzję kosztem zakresu.
BrainSlugs83,

16
  • zmiennoprzecinkowe: ± 1,5 x 10 ^ -45 do ± 3,4 x 10 ^ 38 (~ 7 cyfr znaczących)
  • podwójnie: ± 5,0 x 10 ^ -324 do ± 1,7 x 10 ^ 308 (15-16 cyfr znaczących)
  • dziesiętny: ± 1,0 x 10 ^ -28 do ± 7,9 x 10 ^ 28 (28-29 cyfr znaczących)

9
Różnica to coś więcej niż precyzja. - decimaljest faktycznie przechowywany w formacie dziesiętnym (w przeciwieństwie do podstawy 2, więc nie straci ani nie zaokrągli cyfr z powodu konwersji między dwoma systemami numerycznymi); ponadto decimalnie ma pojęcia wartości specjalnych, takich jak NaN, -0, ∞ lub -∞.
BrainSlugs83

13

To był dla mnie interesujący wątek, ponieważ dzisiaj mieliśmy po prostu paskudny mały błąd dotyczący decimalmniejszej precyzji niż float.

W naszym kodzie C # odczytujemy wartości liczbowe z arkusza kalkulacyjnego Excel, konwertujemy je na decimal, a następnie odsyłamy z decimalpowrotem do usługi, aby zapisać w bazie danych SQL Server .

Microsoft.Office.Interop.Excel.Range cell = 
object cellValue = cell.Value2;
if (cellValue != null)
{
    decimal value = 0;
    Decimal.TryParse(cellValue.ToString(), out value);
}

Teraz, dla prawie wszystkich naszych wartości Excela, działało to pięknie. Ale w przypadku niektórych, bardzo małych wartości Excela, użycie decimal.TryParsecałkowicie utraciło wartość. Jednym z takich przykładów jest

  • cellValue = 0,00006317592

  • Decimal.TryParse (cellValue.ToString (), wartość wyjściowa); // zwróci 0

Dziwnym rozwiązaniem było przekonwertowanie wartości Excela na doublenajpierw, a następnie na decimal:

Microsoft.Office.Interop.Excel.Range cell = 
object cellValue = cell.Value2;
if (cellValue != null)
{
    double valueDouble = 0;
    double.TryParse(cellValue.ToString(), out valueDouble);
    decimal value = (decimal) valueDouble;
    
}

Chociaż doublema mniejszą precyzję niż a decimal, faktycznie zapewniło to, że małe liczby będą nadal rozpoznawane. Z jakiegoś powodu double.TryParsebył w stanie odzyskać tak małe liczby, a jednocześnie decimal.TryParseustawić je na zero.

Dziwny. Bardzo dziwne.


3
Z ciekawości, jaka była pierwotna wartość cellValue.ToString ()? Decimal.TryParse („0.00006317592”, out val) wydaje się działać ...
micahtan

11
-1 Nie zrozum mnie źle, jeśli to prawda, jest to bardzo interesujące, ale jest to osobne pytanie, z pewnością nie jest odpowiedzią na to pytanie.
weston

2
Może dlatego, że komórka Excel zwracała podwójną wartość, a wartość ToString () wynosiła „6.31759E-05”, dlatego decimal.Parse () nie lubił notacji. Założę się, że jeśli sprawdziłeś wartość zwracaną Decimal.TryParse (), byłoby to fałszem.
SergioL

2
@weston Odpowiedzi często uzupełniają inne odpowiedzi, wypełniając niuanse, które przegapili. Ta odpowiedź podkreśla różnicę w zakresie analizowania. To w dużej mierze odpowiedź na pytanie!
Robino,

1
Eee ... decimal.Parse("0.00006317592")działa - dzieje się coś innego. - Być może notacja naukowa?
BrainSlugs83

8

W aplikacjach, takich jak gry i systemy wbudowane, w których zarówno pamięć, jak i wydajność mają krytyczne znaczenie, liczba zmiennoprzecinkowa jest zwykle numerycznym wyborem, ponieważ jest szybsza i dwukrotnie mniejsza. Kiedyś bronią z wyboru były liczby całkowite, ale wydajność zmiennoprzecinkowa wyprzedziła liczbę całkowitą w nowoczesnych procesorach. Dziesiętny jest już gotowy!


Prawie wszystkie nowoczesne systemy, nawet telefony komórkowe, mają sprzętową obsługę podwójnego; a jeśli twoja gra ma nawet prostą fizykę, zauważysz dużą różnicę między double a float. (Na przykład, obliczając prędkość / tarcie w prostym klonie planetoid, podwajanie pozwala, aby przyspieszenie
płynęło

Podwajanie jest również dwukrotnie większe od liczb zmiennoprzecinkowych, co oznacza, że ​​musisz przeżuwać dwukrotnie więcej danych, co obniża wydajność pamięci podręcznej. Jak zawsze, zmierz i postępuj odpowiednio.
yoyo

6

Typy zmiennych Decimal, Double i Float różnią się sposobem przechowywania wartości. Precyzja jest główną różnicą, w której liczba zmiennoprzecinkowa to zmiennoprzecinkowy typ danych o pojedynczej precyzji (32 bity), podwójna to zmiennoprzecinkowy typ danych o podwójnej precyzji (64 bity), a liczba dziesiętna to 128-bitowy zmiennoprzecinkowy typ danych.

Float - 32 bity (7 cyfr)

Podwójny - 64 bity (15-16 cyfr)

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

Więcej o ... różnicy między dziesiętną, zmiennoprzecinkową i podwójną


5

Problem z tymi wszystkimi typami polega na tym, że istnieje pewna niedokładność ORAZ że ten problem może wystąpić przy małych liczbach dziesiętnych, jak w poniższym przykładzie

Dim fMean as Double = 1.18
Dim fDelta as Double = 0.08
Dim fLimit as Double = 1.1

If fMean - fDelta < fLimit Then
    bLower = True
Else
    bLower = False
End If

Pytanie: Jaką wartość zawiera zmienna bLower?

Odpowiedź: Na 32-bitowej maszynie bLower zawiera PRAWDA !!!

Jeśli zastąpię Double przez Decimal, bLower zawiera FAŁSZ, co jest dobrą odpowiedzią.

Podwójnie problem polega na tym, że fMean-fDelta = 1.09999999999, który jest niższy niż 1.1.

Uwaga: Myślę, że ten sam problem z pewnością może istnieć w przypadku innych liczb, ponieważ dziesiętny jest tylko dwukrotnością z większą precyzją, a precyzja ma zawsze limit.

W rzeczywistości Double, Float i Decimal odpowiadają BINARNYM dziesiętnym w języku COBOL!

Szkoda, że ​​inne typy liczbowe zaimplementowane w języku COBOL nie istnieją w .Net. Dla tych, którzy nie znają języka COBOL, istnieje w języku COBOL następujący typ liczbowy

BINARY or COMP like float or double or decimal
PACKED-DECIMAL or COMP-3 (2 digit in 1 byte)
ZONED-DECIMAL (1 digit in 1 byte) 

4

W prostych słowach:

  1. Typy zmiennych Decimal, Double i Float różnią się sposobem przechowywania wartości.
  2. Precyzja jest główną różnicą (zauważ, że to nie jest pojedyncza różnica), gdzie zmiennoprzecinkowa jest typem danych zmiennoprzecinkowych o pojedynczej precyzji (32 bity), podwójna to typ danych zmiennoprzecinkowych o podwójnej precyzji (64 bity), a liczba dziesiętna to 128-bit zmiennoprzecinkowy typ danych.
  3. Tabela podsumowująca:

/==========================================================================================
    Type       Bits    Have up to                   Approximate Range 
/==========================================================================================
    float      32      7 digits                     -3.4 × 10 ^ (38)   to +3.4 × 10 ^ (38)
    double     64      15-16 digits                 ±5.0 × 10 ^ (-324) to ±1.7 × 10 ^ (308)
    decimal    128     28-29 significant digits     ±7.9 x 10 ^ (28) or (1 to 10 ^ (28)
/==========================================================================================
Możesz przeczytać więcej tutaj , Float , Double i Decimal .


Co dodaje ta odpowiedź, która nie jest jeszcze uwzględniona w istniejących odpowiedziach? BTW, twój znak „lub” w wierszu „dziesiętnym” jest niepoprawny: ukośnik na stronie internetowej, z której kopiujesz, wskazuje podział, a nie alternatywę.
Mark Dickinson

1
I zdecydowanie zaprzeczam, że precyzja jest główną różnicą. Główną różnicą jest podstawa: dziesiętny zmiennoprzecinkowy vs. binarny zmiennoprzecinkowy. Ta różnica Decimalnadaje się do zastosowań finansowych i jest głównym kryterium przy podejmowaniu decyzji między Decimala Double. Rzadko zdarza się, że Doubleprecyzja nie wystarcza na przykład do zastosowań naukowych (i Decimalczęsto nie nadaje się do zastosowań naukowych ze względu na ograniczony zasięg).
Mark Dickinson

2

Główną różnicą między nimi jest precyzja.

floatjest 32-bitliczbą, doublejest 64-bitliczbą i decimaljest 128-bitliczbą.


0
  • Dziesiętny 128-bitowy (28-29 cyfr znaczących) W przypadku aplikacji finansowych lepiej jest używać typów dziesiętnych, ponieważ zapewnia wysoki poziom dokładności i łatwe do uniknięcia błędy zaokrąglania Użyj dziesiętnego dla matematyki niecałkowitej, gdzie wymagana jest precyzja (np. pieniądze i waluta)

  • Podwójny 64-bitowy (15-16 cyfr) Typy podwójne są prawdopodobnie najczęściej używanym typem danych dla wartości rzeczywistych, z wyjątkiem obsługi pieniędzy. Użyj podwójnego dla matematyki niecałkowitej, gdzie najbardziej precyzyjna odpowiedź nie jest konieczna.

  • Float 32-bitowy (7 cyfr) Używany jest głównie w bibliotekach graficznych, ponieważ bardzo wysokie wymagania dotyczące mocy obliczeniowych, również w sytuacjach, które mogą znosić błędy zaokrąglania.

Decimalssą znacznie wolniejsze niż a double/float.

Decimalsi Floats/Doublesnie można go porównać bez obsady, Floatsa jednocześnie Doublesmożna.

Decimals zezwól również na kodowanie lub końcowe zera.


-1

Aby zdefiniować dziesiętny, zmiennoprzecinkowy i podwójny w .Net (c #)

musisz podać wartości jako:

Decimal dec = 12M/6;
Double dbl = 11D/6;
float fl = 15F/6;

i sprawdź wyniki.

I Bajty zajmowane przez każdego są

Float - 4
Double - 8
Decimal - 12
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.