Jaki jest najlepszy typ danych do wykorzystania pieniędzy w C #?

426

Jaki jest najlepszy typ danych do wykorzystania pieniędzy w C #?

NotDan
źródło
4
Pomocne mogą być odpowiedzi z tego postu .
ntombela
Oto mapowanie dla wszystkich typów danych: docs.microsoft.com/en-us/dotnet/framework/data/adonet/…
JohnLBevan
Ponadto, jeśli używasz adnotacji danych, using System.ComponentModel.DataAnnotations;[DataType(DataType.Currency)]
włącz

Odpowiedzi:

422

Jak opisano dziesiętnie jako:

Słowo dziesiętne wskazuje 128-bitowy typ danych. W porównaniu do typów zmiennoprzecinkowych, typ dziesiętny ma większą dokładność i mniejszy zakres, co czyni go odpowiednim do obliczeń finansowych i pieniężnych .

Możesz użyć dziesiętnej w następujący sposób:

decimal myMoney = 300.5m;
Lee Treveil
źródło
41
Powinieneś wyjaśnić, co jest ważne w tym linku. Odpowiedź powinna być sama w sobie wystarczająca, a link jako dodatkowe odniesienie lub szczegół. Zobacz stackoverflow.com/help/how-to-answer
TheRubberDuck
2
Tak więc odpowiedź o minimalnej długości może zawierać mniej znaków niż komentarz o minimalnej długości - ciekawe! Nie dlatego, że mam problem z zwięzłą / zwięzłą odpowiedzią, szczególnie gdy jest ona również „głęboka”, ponieważ łączy się z dalszą dyskusją.
B. Clay Shannon
3
Niesamowita odpowiedź i nie uważam, że wymaga dalszych wyjaśnień, ponieważ całkowicie odpowiada na pytanie. Link do dokumentacji MSDN jest dla mnie premią. Brawo!
trnelson
@Leee Treveil, jakie są pieniądze (9.0098), oznacza 4 znaki po punkcie
SAR
114

System.Decimal

Typ wartości dziesiętnej reprezentuje liczby dziesiętne w zakresie od dodatnich 79 228,162,514,264,337,593,543,950,335 do ujemnych 79 228,162,514,264,335,593. Typ wartości dziesiętnej jest odpowiedni do obliczeń finansowych wymagających dużej liczby znaczących cyfr całkowitych i ułamkowych i bez błędów zaokrąglania. Typ dziesiętny nie eliminuje potrzeby zaokrąglania. Raczej minimalizuje błędy wynikające z zaokrąglania.

Chciałbym wskazać na tę doskonałą odpowiedź , zastanawiając się, dlaczego podwójnego nie należy używać.

David Walschots
źródło
68

Użyj wzorca Money z wzorców architektury aplikacji korporacyjnych ; podaj kwotę jako dziesiętną, a walutę jako wyliczenie.

lmsasu
źródło
2
Właściwie to zamierzałem to zasugerować, ale zmieniam Walutę w klasę, abym mógł zdefiniować kurs wymiany (w stosunku do „waluty bazowej”, często dolara amerykańskiego [którego ustawiłem na kurs 1,00]).
Thomas Owens,
5
Dla przyszłych odwiedzających ten wątek (jak ja), teraz jest to: nuget.org/packages/Money i to kołysze!
Korijn
Zastanawiasz się, czy taki typ powinien być strukturą czy klasą. Dziesiętny + an (int) wyliczenie powoduje, że 20 bajtów. Moje pieniądze są wciąż na koncie.
nawfal
Ten Moneynuget ma martwy link do strony projektu, więc ... żadnych dokumentów?
George Mauer,
Problem polega na tym, że jeśli tworzysz własną implementację, musisz dowiedzieć się, jak ją w rzeczywistości utrwalić. Najpopularniejszy ORM (EF) nie ma żadnej obsługi niestandardowych typów danych. Dlatego ktoś proszony jest o naprawdę głębokie chwasty, aby zrobić to, co powinno być dość proste.
George Mauer,
25

Dziesiętny. Jeśli wybierzesz podwójnie, narażasz się na błędy zaokrąglania

SquidScareMe
źródło
8
@Jess doublemoże wprowadzać błędy zaokrąglania, ponieważ zmiennoprzecinkowe nie mogą dokładnie reprezentować wszystkich liczb (np. 0,01 nie ma dokładnej reprezentacji w liczbach zmiennoprzecinkowych). DecimalZ drugiej strony, nie stanowią numery dokładnie . (Kompromis Decimalma mniejszy zakres niż zmiennoprzecinkowy) Zmienny punkt może dać * niezamierzone * błędy zaokrąglania (np 0.01+0.01 != 0.02.). Decimalmoże dać ci błędy zaokrąglania, ale tylko wtedy, gdy o to poprosisz (np. Math.Round(0.01+0.02)zwraca zero)
Ian Boyd
2
@IanBoyd: Wartość „1,57 $” można dokładnie przedstawić (podwójnie) 157. Jeśli ktoś stosuje doublei ostrożnie stosuje skalowanie i zaokrąglanie specyficzne dla dziedziny, w stosownych przypadkach, może to być całkowicie precyzyjne. Jeśli ktoś jest niechlujny w swoim zaokrąglaniu, decimalmoże dać wyniki, które są semantycznie niepoprawne (np. Jeśli doda się wiele wartości, które powinny być zaokrąglone do najbliższego grosza, ale tak naprawdę nie wokół nich w pierwszej kolejności). Jedyną dobrą rzeczą decimaljest to, że skalowanie jest wbudowane.
supercat
1
@ superupat, odnośnie tego komentarza „jeśli dodaje się wiele wartości, które powinny być zaokrąglone do najbliższego grosza, ale tak naprawdę nie są wokół nich pierwsze”, nie widzę, jak float to rozwiązałby. Jest to błąd użytkownika i nie ma nic wspólnego z cyframi IMHO. Rozumiem, ale wydaje mi się, że został zgubiony, głównie dlatego, że IanBoyd stwierdził, że ... jeśli o to poprosisz.
piła
13

Zgadzam się z wzorem Pieniądza: Posługiwanie się walutami jest po prostu zbyt uciążliwe, gdy używasz miejsc po przecinku.

Jeśli utworzysz klasę walutową, możesz umieścić tam całą logikę związaną z pieniędzmi, w tym poprawną metodę ToString () - większą kontrolę nad wartościami parsowania i lepszą kontrolę podziałów.

Ponadto w przypadku klasy walut nie ma szans na niezamierzone pomieszanie pieniędzy z innymi danymi.

Lennaert
źródło
10

Inną opcją (zwłaszcza jeśli wybierasz własną klasę) jest użycie int lub int64 i oznaczenie czterech niższych cyfr (lub nawet 2) jako „po prawej stronie przecinka”. Więc „na krawędziach” będziesz potrzebować trochę „* 10000” przy wejściu i trochę „/ 10000” przy wyjściu. Jest to mechanizm przechowywania używany przez SQL Server Microsoftu, patrz http://msdn.microsoft.com/en-au/library/ms179882.aspx

Zaletą tego jest to, że wszystkie sumowania można dokonać za pomocą (szybkiej) arytmetyki liczb całkowitych.

dsz
źródło
7

Większość aplikacji, z którymi pracowałem, decimalreprezentuje pieniądze. Jest to oparte na założeniu, że wniosek nigdy nie będzie dotyczył więcej niż jednej waluty.

To założenie może opierać się na innym założeniu, że aplikacja nigdy nie będzie używana w innych krajach o różnych walutach. Widziałem przypadki, w których okazało się to nieprawdą.

Teraz to założenie jest kwestionowane w nowy sposób: nowe waluty, takie jak Bitcoin, stają się coraz bardziej powszechne i nie są specyficzne dla żadnego kraju. Nie jest nierealne, że aplikacja używana tylko w jednym kraju może nadal wymagać obsługi wielu walut.

Niektórzy powiedzą, że tworzenie, a nawet używanie tylko dla pieniędzy, to „pozłacanie” lub zwiększanie złożoności poza znane wymagania. Definitywnie się z tym nie zgadzam. Im bardziej wszechobecna jest koncepcja w Twojej domenie, tym ważniejsze jest podjęcie rozsądnego wysiłku, aby użyć właściwej abstrakcji z góry. Jeśli chcesz zobaczyć złożoność, spróbuj pracować w aplikacji, która była używana, decimala teraz Currencyobok każdej decimalwłaściwości znajduje się dodatkowa właściwość.

Jeśli użyjesz niewłaściwej abstrakcji z góry, zastąpienie jej później będzie sto razy więcej pracy. Oznacza to potencjalne wprowadzenie defektów do istniejącego kodu, a najlepsze jest to, że defekty te prawdopodobnie będą obejmować kwoty pieniężne, transakcje z pieniędzmi lub po prostu cokolwiek z pieniędzmi.

I nie jest tak trudno używać czegoś innego niż dziesiętny. Google „nuget money type” i zobaczysz, że wielu programistów stworzyło takie abstrakcje (w tym ja). To proste. Jest to tak proste, jak użycie DateTimezamiast przechowywania daty w string.

Scott Hannen
źródło
5

Stwórz własną klasę. Wydaje się to dziwne, ale typ .Net jest nieodpowiedni do obsługi różnych walut.

Noel Kennedy
źródło