Jakiego typu danych powinienem użyć dla waluty w grze?

96

Czy w prostej grze symulacyjnej dla biznesu (wbudowanej w Javę + Slick2D) aktualna kwota pieniędzy gracza powinna być przechowywana w postaci innej floatkarty int?

W moim przypadku większość transakcji będzie korzystała z centów (0,50 USD, 1,20 USD itp.) I będą obejmowały proste obliczenia stopy procentowej.

Widziałem ludzi mówiących, że nigdy nie powinieneś używać floatwaluty, a także ludzi, którzy twierdzą, że nigdy nie powinieneś używać intwaluty. Czuję, że powinienem zastosować inti zaokrąglić wszelkie niezbędne obliczenia procentowe. Czego powinienem użyć?

Lucas Tulio
źródło
2
To pytanie zostało omówione na StackOverflow. Wygląda na to, że BigDecimal jest prawdopodobnie najlepszym rozwiązaniem. stackoverflow.com/questions/285680/…
Ade Miller,
2
Czy Java nie ma takiego Currencytypu jak Delphi, który używa skalowanej matematyki stałoprzecinkowej, aby dać ci matematy dziesiętne bez problemów z precyzją nieodłącznych od zmiennoprzecinkowych?
Mason Wheeler,
1
To jest gra. Rachunkowość nie zlinczuje cię zaokrąglony grosz, a zatem zwykłe powody, dla których nie używasz zmiennoprzecinkowego dla waluty, nie mają znaczenia.
Loren Pechtel 22.12.12
@MasonWheeler: Java ma BigDecimaltego rodzaju problemy.
Martin Schröder,

Odpowiedzi:

92

Możesz użyć inti rozważyć wszystko w centach. 1,20 USD to zaledwie 120 centów. Na wyświetlaczu wstawiasz przecinek tam, gdzie należy.

Obliczenia odsetek byłyby po prostu obcięte lub zaokrąglone w górę. Więc

newAmt = round( 120 cents * 1.04 ) = round( 124.8 ) = 125 cents

W ten sposób nie będziesz mieć bałaganu po przecinku. Możesz wzbogacić się, dodając do swojego konta bankowego niezliczone pieniądze (z powodu zaokrągleń)

Bobobobo
źródło
3
Jeśli używasz, roundnie ma prawdziwego powodu, aby rzucić na to int, czy jest?
sam hocevar,
19
+1. Liczby zmiennoprzecinkowe psują porównania równości, trudniej je poprawnie sformatować i z czasem wprowadzają śmieszne błędy zaokrąglania. Lepiej robić to wszystko samemu, aby później nie narzekać. To naprawdę nie jest dużo pracy.
Dobes Vandermeer,
+1. Brzmi jak dobry sposób na rozpoczęcie mojej gry. Sam zajmę się zaokrąglaniem ints i trzymam się z dala od wszystkich problemów z pływakiem.
Lucas Tulio
Tylko zrzeczenie się: Zmieniłem poprawną odpowiedź na tę, ponieważ ostatecznie było to, czego użyłem. Jest to o wiele prostsze i w kontekście tak prostej gry niezauważone miejsca po przecinku nie mają tak naprawdę znaczenia.
Lucas Tulio
3
Należy jednak używać punktów bazowych, a nie centów (gdzie 100 punktów bazowych = 1 cent) o współczynniku 10 000 zamiast 100. Jest to wymagane przez GAAP dla wszystkich aplikacji finansowych, bankowych lub rachunkowych.
Pieter Geerkens
66

Okej, wskoczę.

Moja rada: to gra. Uspokój się i użyj double.

Oto moje uzasadnienie:

  • floatma problem z precyzją, który pojawia się przy dodawaniu jednostek do milionów, więc chociaż może być łagodny, unikałbym tego typu. doublezaczyna się problemy z kwintillonami (miliard miliardów).
  • Ponieważ będziesz mieć stopy procentowe, i tak będziesz potrzebować teoretycznej nieskończonej precyzji: przy stopach procentowych 4%, 100 USD zmieni się na 104, następnie 108,16 USD, a następnie 112,4864 itd. To czyni inti jest longbezużyteczne, ponieważ nie wiesz, gdzie się zatrzymać miejsca dziesiętne.
  • BigDecimalda ci dowolną precyzję, ale stanie się boleśnie powolny, chyba że kiedyś dokonasz precyzji. Jakie są zasady zaokrąglania? Jak wybrać, gdzie się zatrzymać? Czy warto mieć więcej precyzyjnych bitów niż double? Nie wierzę.

Powodem stosowania arytmetyki punktu stałego w aplikacjach finansowych jest to, że są deterministyczne. Reguły zaokrąglania są doskonale zdefiniowane, czasem przez prawo, i muszą być ściśle stosowane, ale w pewnym momencie nadal występuje zaokrąglanie . Każdy argument przemawiający za danym typem opartym na precyzji jest prawdopodobnie fałszywy. Wszystkie typy mają problemy z precyzją w rodzaju obliczeń, które zamierzasz wykonać.

Praktyczne przykłady

Widzę sporo komentarzy twierdzących, że nie zgadzam się z zaokrągleniem lub precyzją. Oto kilka dodatkowych przykładów ilustrujących to, co mam na myśli.

Przechowywanie : jeśli jednostką podstawową jest cent, prawdopodobnie będziesz chciał zaokrąglić do najbliższego centa podczas przechowywania wartości:

void SetValue(double v) { m_value = round(v * 100.0) / 100.0; }

Przy zastosowaniu tej metody nie będziesz mieć absolutnie żadnych problemów z zaokrąglaniem, których nie miałbyś również w przypadku liczb całkowitych.

Pobieranie : wszystkie obliczenia można wykonać bezpośrednio na podwójnych wartościach, bez konwersji:

double value = data.GetValue();
value = value / 3.0 * 12.0;
[...]
data.SetValue(value);

Zauważ, że powyższy kod nie działa, jeśli zastąpi doublesię int64_t: nie będzie niejawna konwersja double, a następnie obcięcie do int64_t, z możliwością utraty information.data.GetValue ()

Porównywanie : porównania są jedną rzeczą, którą należy uzyskać w przypadku typów zmiennoprzecinkowych. Sugeruję użycie metody porównania, takiej jak ta:

/* Are values equal to a tenth of a cent? */
bool AreCurrencyValuesEqual(double a, double b) { return abs(a - b) < 0.001; }

Zaokrąglanie uczciwości

Załóżmy, że masz na koncie 9,99 USD z 4% odsetkami. Ile powinien zarobić gracz? Zaokrąglając liczbę całkowitą, otrzymujesz 0,03 $; zaokrąglanie zmiennoprzecinkowe daje 0,04 USD. Uważam, że to drugie jest bardziej sprawiedliwe.

sam hocevar
źródło
4
+1. Jedyne wyjaśnienie, które chciałbym dodać, to to, że aplikacje finansowe są zgodne z określonymi z góry określonymi wymaganiami, podobnie jak algorytm zaokrąglania. Jeśli gra ma charakter kasynowy, jest zgodna z podobnymi standardami, a zatem podwójne nie wchodzi w grę.
Ivaylo Slavov,
2
Nadal musisz pomyśleć o regułach zaokrąglania, aby sformatować interfejs użytkownika. Ponieważ zawsze jest ułamek graczy, którzy spróbują traktować grę jako arkusz kalkulacyjny, jeśli nie chcesz poradzić sobie z ludźmi podskakującymi i krzyczącymi, gdy zauważą, że Twój backend nie używa tej samej precyzji, co interfejs użytkownika, co powoduje wyświetlanie „złych” wyników najlepszą opcją dla ich zachowania w ciszy jest użycie tej samej wewnętrznej i zewnętrznej reprezentacji, co oznacza użycie formatu stałego punktu. Jeśli wydajność nie stanie się problemem, najlepszym wyborem jest BigDecimal.
Dan Neely,
10
Nie zgadzam się z tą odpowiedzią. Pojawiają się problemy z zaokrąglaniem, a jeśli spadną, a jeśli nie używasz żadnego dodatkowego zaokrąglania, 1,00 USD może nagle stać się 0,99 USD. Ale co najważniejsze, powielanie dowolnych liczb dziesiętnych z dokładnością jest (prawie) zupełnie nieistotne. Jesteśmy prawie w 2013 roku i jeśli nie robisz dużych faktoryzacji, uruchamiasz rzeczy lub logarytmy na kilku milionach liczb z tysiącami cyfr, nigdy nie zauważysz obniżenia wydajności. W każdym razie proste jest zawsze najlepsze, dlatego zalecam przechowywanie wszystkich liczb w centach. W ten sposób wszyscy są int_64ts.
Panda Pajama,
8
@ Sam podczas ostatniego sprawdzania mojego konta bankowego moje saldo nie skończyło się na 0,0000000000000001. Systemy prawdziwej waluty muszą działać z niepodzielnymi jednostkami, a to poprawnie reprezentowane przez liczby całkowite. Jeśli musisz poprawnie dokonywać podziałów walut (które w rzeczywistości nie są tak powszechne w prawdziwym świecie finansowym), musisz je robić, aby pieniądze nie zostały utracone. Na przykład 10/3 = 3+3+4lub 10/3 = 3+3+3 (+1). Dla wszystkich innych operacji liczby całkowite działają idealnie, w przeciwieństwie do liczb zmiennoprzecinkowych, które mogą powodować problemy z zaokrąglaniem w dowolnej operacji.
Panda Pajama
2
@SamHocevar 999 * 4/100. Jeżeli nie jest to określone w przepisach, załóżmy, że wszystkie zaokrąglenia zostaną przeprowadzone na korzyść banków.
Dan Neely,
21

Typy zmiennoprzecinkowe w Javie ( float, double) nie są dobrą reprezentacją walut z jednego głównego powodu - występuje błąd maszynowy w zaokrąglaniu. Nawet jeśli to prosta kalkulacja zwraca liczbę całkowitą - jak 12.0/2(6,0), przy czym zmiennoprzecinkowe może błędnie runda to (z powodu tho konkretnej reprezentacji tych typów pamięci) jako 6.0000000000001lub 5.999999999999998lub podobny. Jest to wynik specyficznego zaokrąglenia maszyny występującego w procesorze i jest unikalny dla komputera, który go obliczył. Zwykle rzadko jest problem z obsługą tych wartości, ponieważ błąd jest dość zaniedbany, ale wyświetlenie go użytkownikowi nie jest łatwe.

Możliwym rozwiązaniem tego byłoby użycie niestandardowych implementacji typu danych zmiennoprzecinkowych, takich jak BigDecimal. Obsługuje lepsze mechanizmy obliczeniowe, które przynajmniej izolują błędy zaokrąglania, które nie są specyficzne dla maszyny, ale są wolniejsze pod względem wydajności.

Jeśli potrzebujesz wysokiej wydajności, lepiej trzymaj się prostych typów. Jeśli operujesz ważnymi danymi finansowymi , a każdy cent jest ważny (np. Aplikacja Forex lub gra kasynowa), polecam użyć Longlub long. Longpozwoli ci obsługiwać duże ilości i dobrą precyzję. Załóżmy, że potrzebujesz, powiedzmy, 4 cyfr po przecinku, wystarczy pomnożyć tę kwotę przez 10000. Mając doświadczenie w opracowywaniu gier kasynowych online, widziałem, Longże często jestem używany do przedstawiania pieniędzy w centach . W aplikacjach Forex dokładność jest ważniejsza, więc potrzebujesz większego mnożnika - wciąż liczby całkowite są wolne od problemów z zaokrąglaniem maszyny (oczywiście zaokrąglania ręcznego, jak w 3/2, powinieneś sobie poradzić).

Dopuszczalną opcją byłoby użycie standardowych typów zmiennoprzecinkowych - Floata Doublejeśli wydajność jest ważniejsza niż dokładność do setnych centa. Następnie, zgodnie z logiką wyświetlania, wystarczy użyć predefiniowanego formatowania , aby brzydota potencjalnego zaokrąglenia maszyny nie dotarła do użytkownika.

Ivaylo Slavov
źródło
4
+1 Szczegół: „Załóżmy, że potrzebujesz, powiedzmy, 4 cyfry po przecinku, wystarczy pomnożyć kwotę przez 1000”. -> Nazywa się to punktem stałym.
Laurent Couvidou,
1
@Sam, numery podano dla przykładowej stawki. Mimo to osobiście widziałem dziwne wyniki przy tak prostych liczbach, ale nie jest to częsty scenariusz. Kiedy pojawiają się zmiennoprzecinkowe, jest to prawdopodobnie bardziej prawdopodobne, ale bardziej mało prawdopodobne
Ivaylo Slavov
2
@Ivaylo Slavov, zgadzam się z tobą. W mojej odpowiedzi właśnie wydałem opinię. Uwielbiam dyskutować i mam ochotę podjąć właściwą decyzję. Dziękuję również za twoją opinię. :)
Md Mahbubur Rahman,
1
@SamHocevar: To rozumowanie nie działa we wszystkich przypadkach. Patrz: ideone.com/nI2ZOK
Samaursa,
1
@SamHocevar: To też nie jest gwarantowane. Liczby w zakresie, które wciąż są liczbami całkowitymi, zaczynają się kumulować błąd przy pierwszym dzieleniu: ideone.com/AKbR7i
Samaursa,
17

W przypadku gier na małą skalę i tam, gdzie szybkość procesu ważna jest pamięć (ze względu na precyzję lub praca z koprocesorem matematycznym może boleśnie spowolnić), wystarczy podwójna .

Ale w przypadku gier na dużą skalę (na przykład gier społecznościowych) i tam, gdzie szybkość procesu, pamięć nie jest ograniczona, BigDecimal jest lepszy. Ponieważ tutaj

  • int lub długo dla obliczeń pieniężnych.
  • liczba zmiennoprzecinkowa i liczba podwójna nie mogą dokładnie reprezentować większości 10 liczb rzeczywistych.

Zasoby:

Od https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency

Ponieważ liczby zmiennoprzecinkowe i liczby podwójne nie mogą dokładnie reprezentować większości 10 liczb rzeczywistych podstawowych.

Tak działa liczba zmiennoprzecinkowa IEEE-754: poświęca ona trochę znakowi, kilka bitów do przechowywania wykładnika, a resztę dla rzeczywistej części. Prowadzi to do reprezentacji liczb w formie podobnej do 1,45 * 10 ^ 4; z wyjątkiem tego, że zamiast podstawy 10, to dwa.

Wszystkie rzeczywiste liczby dziesiętne mogą być postrzegane jako dokładne ułamki potęgi dziesięciu. Na przykład 10,45 to naprawdę 1045/10 2. I tak jak niektóre ułamki nie mogą być reprezentowane dokładnie jako ułamek potęgi dziesięciu (przychodzi na myśl 1/3), niektóre z nich nie mogą być przedstawiane dokładnie jako ułamek potęgi dwóch. Jako prosty przykład po prostu nie możesz przechowywać 0.1 w zmiennej zmiennoprzecinkowej. Otrzymasz najbliższą możliwą do przedstawienia wartość, która jest jak 0,099999999999999999996, a oprogramowanie zaokrągli ją do 0,1 podczas wyświetlania.

Jednak w miarę wykonywania kolejnych dodań, odejmowań, mnożenia i podziałów na niedokładnych liczbach tracisz coraz większą precyzję w miarę sumowania się drobnych błędów. To sprawia, że ​​liczba zmiennoprzecinkowa i liczba podwójna są nieodpowiednie do radzenia sobie z pieniędzmi, gdzie wymagana jest idealna dokładność.

Z Bloch, J., Effective Java, wyd. 2, pozycja 48:

The float and double types are particularly ill-suited for 

obliczenia pieniężne, ponieważ niemożliwe jest przedstawienie 0,1 (lub jakiejkolwiek innej ujemnej potęgi dziesięciu) jako liczby zmiennoprzecinkowej lub podwójnej.

For example, suppose you have $1.03 and you spend 42c. How much money do you have left?

System.out.println(1.03 - .42);

prints out 0.6100000000000001.

The right way to solve this problem is to use BigDecimal, 

int lub długo dla obliczeń pieniężnych.

Zobacz także

Md Mahbubur Rahman
źródło
Nie zgadzam się tylko z tym, że część długa nadaje się do małych obliczeń. Z mojego prawdziwego doświadczenia wynika, że ​​jest wystarczający do obsługi dobrej precyzji i dużych kwot pieniędzy. Tak, może być trochę kłopotliwy w użyciu, ale pokonuje BigDecimal w dwóch kluczowych punktach - 1) jest szybszy (BigDecimal używa oprogramowania zaokrąglania, które jest znacznie wolniejsze niż wbudowane zaokrąglanie procesora używane w float / double) i 2) Trudniej jest zachować BigDecimal w bazie danych bez utraty precyzji - nie wszystkie bazy danych obsługują taki „niestandardowy” typ. Długi jest dobrze znany i szeroko obsługiwany we wszystkich głównych bazach danych.
Ivaylo Slavov
@Ivaylo Slavov, przepraszam za mój błąd. Zaktualizowałem swoją odpowiedź.
Md Mahbubur Rahman,
Przeprosiny nie były konieczne, aby podać inne podejście do tego samego problemu :)
Ivaylo Slavov,
2
@Ivaylo Slavov, zgadzam się z tobą. W mojej odpowiedzi właśnie wydałem opinię. Uwielbiam dyskutować i mam ochotę podjąć właściwą decyzję. Dziękuję również za twoją opinię. :)
Md Mahbubur Rahman,
celem tej witryny jest wyjaśnienie problemów i pomoc PO w podjęciu właściwej decyzji, w zależności od konkretnego scenariusza. Wszystko, co możemy zrobić, to pomóc przyspieszyć ten proces :)
Ivaylo Slavov,
13

Aby zapisać swoją walutę longi obliczyć swoją walutę double, przynajmniej jako zapasową. Chcesz, aby wszystkie transakcje odbywały się jako long.

Powodem, dla którego chcesz przechowywać swoją walutę, longjest to, że nie chcesz stracić żadnej waluty.

Załóżmy, że używasz doublei nie masz pieniędzy. Ktoś daje ci trzy dziesięciocentówki, a następnie bierze je z powrotem.

You:       0.1+0.1+0.1-0.1-0.1-0.1 = 2.7755575615628914E-17

Cóż, to nie jest takie fajne. Może ktoś z 10 $ chce przekazać swoją fortunę, najpierw dając ci trzy dziesięciocentówki, a następnie 9,70 $ komuś innemu.

Them: 10.0-0.1-0.1-0.1-9.7 = 1.7763568394002505E-15

A potem oddajesz im dziesięciocentówki:

Them: ...+0.1+0.1+0.1 = 0.3000000000000018

To jest po prostu zepsute.

Teraz użyjmy długiego, a my będziemy śledzić dziesiąte centy (czyli 1 = 0,001 $). Dajmy każdemu na świecie miliard sto dwanaście milionów siedemdziesiąt pięć tysięcy sto czterdzieści trzy dolary:

Us: 7000000000L*1112075143000L = 1 894 569 218 048

Um, czekaj, możemy dać każdemu ponad miliard dolarów i wydać tylko nieco ponad dwa? Przepełnienie jest tutaj katastrofą.

Tak więc, ilekroć obliczasz kwotę, którą chcesz przelać, użyj jej doublei Math.roundzdobądź long. Następnie napraw salda (dodaj i odejmij oba konta) za pomocą long.

Twoja gospodarka nie wycieknie i zwiększy się do miliardów dolarów.

Są trudniejsze problemy - na przykład, co robisz, jeśli dokonujesz dwudziestu płatności? * - ale to powinno zacząć.

* Obliczasz, co to jest jedna płatność, w zaokrągleniu do long; następnie pomnóż przez 20.0i sprawdź, czy jest w zasięgu; jeśli tak, pomnóż płatność przez, 20Laby uzyskać kwotę potrąconą z salda. Zasadniczo wszystkie transakcje muszą być obsługiwane jako long, więc naprawdę trzeba podsumować wszystkie poszczególne transakcje; możesz pomnożyć jako skrót, ale musisz upewnić się, że nie dodajesz błędów zaokrąglania i nie przepełniasz, co oznacza, że ​​musisz sprawdzić doubleprzed wykonaniem prawdziwych obliczeń long.

Rex Kerr
źródło
8

Chciałbym powiedzieć, że każda wartość, która może być wyświetlana użytkownikowi, prawie zawsze powinna być liczbą całkowitą. Pieniądze są tylko najbardziej znanym tego przykładem. Zadanie potworowi 225 obrażeń czterokrotnie 225 i stwierdzenie, że pozostało mu jeszcze 1 PW, odejmie od doświadczenia tak samo, jak stwierdzenie, że jesteś niewidzialnym ułamkiem grosza, który czegoś nie daje.

Z technicznego punktu widzenia warto zauważyć, że nie trzeba wracać do pływaków, aby robić zaawansowane rzeczy, takie jak zainteresowanie. Tak długo, jak masz wystarczającą ilość miejsca w wybranym typie liczb całkowitych, mnożenie i dzielenie będą oznaczać mnożenie przez liczbę dziesiętną, na przykład, aby dodać 4%, w zaokrągleniu w dół:

number=(number*104)/100

Aby dodać 4%, w zaokrągleniu do standardowych konwencji:

number=(number*104+50)/100

Nie ma tu niedokładności punktu zmiennoprzecinkowego, zaokrąglenie zawsze dzieli się dokładnie na .5znaku.

Edytuj prawdziwe pytanie:

Widząc, jak debata upadł zacznę myśleć, że przedstawiając to, co jest pytanie o może być bardziej przydatne niż zwykły int/ floatodpowiedź. U podstaw pytania nie chodzi o typy danych, lecz o przejęcie kontroli nad szczegółami programu.

Użycie liczby całkowitej do przedstawienia wartości niecałkowitej zmusza programistę do zajęcia się szczegółami implementacji. „Jakiej precyzji użyć?” i „W jaki sposób zaokrąglić?” są pytania, na które należy udzielić wyraźnej odpowiedzi.

Z drugiej strony liczba zmiennoprzecinkowa nie zmusza programisty do zmartwień, robi już prawie wszystko, czego można by oczekiwać. Ponieważ jednak liczba zmiennoprzecinkowa nie jest nieskończoną precyzją, nastąpi pewne zaokrąglenie, a to zaokrąglenie jest dość nieprzewidywalne.

Co dzieje się przy jednym użyciu zmiennoprzecinkowych i chcesz przejąć kontrolę nad zaokrąglaniem? Okazuje się, że jest prawie niemożliwe. Jedynym sposobem, aby zmiennoprzecinkowe było naprawdę przewidywalne, jest użycie tylko wartości, które mogą być reprezentowane w całych 2^nliterach. Ale ta konstrukcja sprawia, że ​​spławiki są dość trudne w użyciu.

Tak więc odpowiedź na proste pytanie brzmi: jeśli chcesz przejąć kontrolę, użyj liczb całkowitych, jeśli nie, użyj liczb zmiennoprzecinkowych.

Ale omawiane pytanie jest tylko inną formą pytania: czy chcesz przejąć kontrolę?

aaaaaaaaaaaa
źródło
5

Nawet jeśli to „tylko gra”, użyłbym Money Pattern od Martina Fowlera, popartego długim.

Dlaczego?

Lokalizacja (L10n): Za pomocą tego wzoru możesz łatwo zlokalizować swoją walutę gry. Pomyśl o starych grach potentata, takich jak „Transport Tycoon”. Pozwalają graczowi łatwo zmienić walutę w grze (np. Z funta brytyjskiego na dolara amerykańskiego), aby spełnić warunki rzeczywistej waluty świata.

I

Długi typ danych jest liczbą całkowitą z dopełnieniem dwójki ze znakiem 64-bitowym. Ma minimalną wartość -9 223 372,036,854,775,808 i maksymalną wartość 9 223 372,036,854,775,807 (włącznie) ( samouczki Java )

Oznacza to, że możesz przechowywać 9 000 razy bieżącą podaż pieniądza M2 w USA (~ 10 000 miliardów dolarów). Dając ci wystarczająco dużo miejsca na użycie dowolnej innej światowej waluty, prawdopodobnie nawet tych, którzy mieli / mają hiperinflację (jeśli jesteś ciekawy, zobacz niemiecką inflację po I wojnie światowej , gdzie 1 funt chleba wynosił 3 000 000 000 marek)

Długie jest łatwe do utrzymania, bardzo szybkie i powinno dać ci wystarczająco dużo miejsca na wykonanie wszystkich obliczeń odsetek przy użyciu tylko arytmetyki liczb całkowitych, odpowiedź e-biznesu wyjaśnia, jak to zrobić.

grprado
źródło
2

Ile pracy chcesz w to włożyć? Jak ważna jest dokładność? Czy zależy Ci na śledzeniu błędów ułamkowych, które występują podczas zaokrąglania i niedokładności przedstawiania liczb dziesiętnych w systemie dwójkowym?

Ostatecznie spędzam trochę więcej czasu na kodowaniu i wdrażaniu testów jednostkowych dla „przypadków narożnych” i znanych spraw problematycznych - więc pomyślałbym o zmodyfikowanym BigInteger, który obejmuje dowolnie duże kwoty i zachowuje bity ułamkowe za pomocą BigDecimal lub część BigRational (dwie BigInteger, jedna dla mianownika, a druga dla licznika) - i zawiera kod, aby część ułamkowa była ułamkiem rzeczywistym poprzez (być może tylko okresowe) dodawanie dowolnej części niefrakcyjnej do głównej BigInteger. Następnie wewnętrznie śledziłbym wszystko w centach, tylko po to, aby części ułamkowe nie były uwzględniane w obliczeniach GUI.

Prawdopodobnie sposób na skomplikowanie dla (prostej) gry - ale dobry dla biblioteki opublikowanej jako open source! Musiałbym tylko wymyślić sposoby utrzymania dobrej wydajności w przypadku bitów ułamkowych ...

Richard Arnold Mead
źródło
1

Na pewno nie unosi się. Mając tylko 7 cyfr, masz tylko dokładność:

12 345,67 123 456,7x

Widzisz, już mając 10 ^ 5 dolarów, tracisz poczucie grosza. double jest użyteczny do celów gry, a nie w prawdziwym życiu z powodu obaw o dokładność.

Ale długi (a przez to mam na myśli 64-bitowy lub długi długi w C ++) nie wystarczy, jeśli zamierzasz śledzić transakcje i sumować je. Wystarczy utrzymać udziały netto dowolnej firmy, ale wszystkie transakcje na rok mogą się przepełnić. To zależy od tego, jak duży jest Twój finansowy świat w grze.

Dow
źródło
0

Kolejnym rozwiązaniem, które tu dodam, jest zarabianie pieniędzy. Klasa byłaby czymś podobnym (mogłaby nawet po prostu być strukturą).

class Money
{
     string currencyType; //the type of currency example USD could be an enum as well
     bool isNegativeValue;
     unsigned long int wholeUnits;
     unsigned short int partialUnits;
}

Pozwoliłoby to przedstawić centy w tym przypadku jako liczbę całkowitą, a całe dolary jako liczbę całkowitą. Ponieważ masz oddzielną flagę za ujemną, możesz użyć liczb całkowitych bez znaku dla swoich wartości, co podwaja możliwą ilość (możesz także napisać klasę liczb, która stosuje ten pomysł do liczb, aby uzyskać NAPRAWDĘ duże liczby). Wszystko, co musisz zrobić, to przeciążenie operatorów matematycznych i możesz użyć tego jak każdego starego typu danych. Zajmuje więcej pamięci, ale tak naprawdę rozszerza granice wartości.

Można to dodatkowo rozszerzyć o takie rzeczy, jak liczba częściowych jednostek na całe jednostki, dzięki czemu możesz mieć waluty, które dzielą się na coś innego niż 100 jednostek podrzędnych, w tym przypadku centów, za dolara. Na przykład możesz mieć 125 floopie, które tworzą jeden flooper.

Edytować:

Rozwinę pomysł, że możesz mieć również tabelę przeglądową, do której klasa pieniędzy może uzyskać dostęp, która zapewniłaby kurs dla jej waluty względem wszystkich innych walut. Możesz to wbudować w funkcje przeciążania operatora. Dlatego jeśli spróbujesz automatycznie dodać 4 USD do 4 funtów brytyjskich, automatycznie otrzymasz 6,6 funta (w momencie pisania).

Azaral
źródło
-1

Użyj klasy, to najlepszy sposób. Możesz ukryć realizację swoich pieniędzy, możesz zmieniać podwójne / długie, co tylko chcesz w dowolnym momencie.

Przykład

class Money
{
    private long count;//Store here what ever you want in what type you want i suggest long or int
    //methods constructors etc.
    public String print()
    {
        return count/100.F; //convert this to string
    }
}

W swoim kodzie używaj po prostu gettera, to jest lepszy sposób i myślę, że możesz przechowywać bardziej przydatne metody.

Dawid Drozd
źródło