Sollte in einem einfachen Geschäftssimulationsspiel (in Java + Slick2D integriert) der aktuelle Geldbetrag eines Spielers als float
oder als int
oder als etwas anderes gespeichert werden ?
In meinem Anwendungsfall werden für die meisten Transaktionen Cents verwendet (0,50 USD, 1,20 USD usw.), und einfache Zinsberechnungen sind erforderlich.
Ich habe Leute gesehen, die sagten, Sie sollten niemals float
als Währung verwenden, und Leute, die sagten, Sie sollten niemals int
als Währung verwenden. Ich habe das Gefühl, ich sollte int
alle erforderlichen Prozentberechnungen verwenden und runden. Was soll ich benutzen?
Currency
Typ wie Delphi's, der skalierte Festkomma-Mathematik verwendet, um Dezimal-Mathematik ohne die mit Gleitkomma verbundenen Genauigkeitsprobleme zu erhalten?BigDecimal
für diese Art von Problemen.Antworten:
Sie können
int
alles in Cent verwenden und berücksichtigen. 1,20 US-Dollar sind nur 120 Cent. Bei der Anzeige geben Sie die Dezimalstelle dort ein, wo sie hingehört.Zinsberechnungen würden nur entweder abgeschnitten oder aufgerundet. Damit
Auf diese Weise bleiben keine unordentlichen Dezimalstellen immer hängen. Sie könnten reich werden, indem Sie das nicht angerechnete Geld (aufgrund von Abrundungen) auf Ihr eigenes Bankkonto einzahlen
quelle
round
gibt es keinen wirklichen Grund, auf den Sie sich besinnen solltenint
, gibt es dann einen?Okay, ich werde hineinspringen.
Mein Rat: Es ist ein Spiel. Nehmen Sie es einfach und verwenden Sie
double
.Hier ist meine Begründung:
float
Es gibt ein Präzisionsproblem, das beim Hinzufügen von Einheiten zu Millionen auftritt. Obwohl es harmlos ist, würde ich diesen Typ vermeiden.double
fängt nur an, Probleme um die Quintillons (eine Milliarde Milliarden) zu bekommen.int
undlong
unbrauchbar, weil Sie nicht wissen, wo Sie aufhören sollen die Dezimalstellen.BigDecimal
gibt Ihnen eine willkürliche Präzision, wird aber schmerzhaft langsam, es sei denn, Sie klemmen die Präzision zu einem bestimmten Zeitpunkt. Was sind die Rundungsregeln? Wie wählen Sie, wo Sie anhalten möchten? Lohnt es sich, mehr Präzisionsbits zu haben alsdouble
? Ich glaube nicht.Der Grund, warum Festkomma-Arithmetik in Finanzanwendungen verwendet wird, liegt darin, dass sie deterministisch sind. Die Rundungsregeln sind, manchmal gesetzlich, perfekt definiert und müssen strikt angewendet werden, aber irgendwann tritt immer noch eine Rundung auf. Jedes Argument für einen bestimmten Typ, das auf Präzision basiert, ist wahrscheinlich falsch. Alle Typen haben Genauigkeitsprobleme mit der Art der Berechnungen, die Sie durchführen werden.
Praxisbeispiele
Ich sehe etliche Kommentare, die Dinge über Rundung oder Genauigkeit behaupten, mit denen ich nicht einverstanden bin. Hier sind einige zusätzliche Beispiele, um zu veranschaulichen, was ich meine.
Speichern : Wenn Ihre Basiseinheit der Cent ist, möchten Sie beim Speichern der Werte wahrscheinlich auf den nächsten Cent runden:
Sie werden absolut keine Rundungsprobleme bekommen, wenn Sie diese Methode anwenden, die Sie nicht auch mit einem Integer-Typ hätten.
Abrufen : Alle Berechnungen können direkt mit den Doppelwerten ohne Konvertierungen durchgeführt werden:
Beachten Sie, dass der obige Code nicht , wenn Sie ersetzen funktioniert
double
mitint64_t
: Es wird eine implizite Konvertierung seindouble
, dann Abschneiden zuint64_t
, mit einem möglichen Verlust von information.data.GetValue ()Vergleichen : Vergleiche sind das Richtige für Gleitkommatypen. Ich schlage vor, eine Vergleichsmethode wie diese zu verwenden:
Rundung der Fairness
Angenommen, Sie haben 9,99 USD auf dem Konto mit 4% Zinsen. Wie viel sollte der Spieler verdienen? Mit Integer-Rundung erhalten Sie $ 0,03; mit Gleitkomma-Rundung erhalten Sie 0,04 $. Letzteres halte ich für fairer.
quelle
int_64t
s.10/3 = 3+3+4
oder10/3 = 3+3+3 (+1)
. Bei allen anderen Operationen funktionieren Ganzzahlen im Gegensatz zu Gleitkommazahlen, die bei jeder Operation zu Rundungsproblemen führen können, einwandfrei.Gleitkommatypen in Java (
float
,double
) sind für Währungen aus einem Hauptgrund nicht gut darstellbar: Es liegt ein Maschinenfehler bei der Rundung vor. Selbst wenn eine einfache Berechnung gibt eine ganze Anzahl - wie12.0/2
(6,0), die Gleitkomma könnte falsch um ihn herum (aufgrund tho der spezifischen Darstellung dieser Typen im Speicher) wie6.0000000000001
oder5.999999999999998
oder ähnliches. Dies ist ein Ergebnis der spezifischen Maschinenrundung, die im Prozessor auftritt und für den Computer, der sie berechnet hat, eindeutig ist. Normalerweise ist es selten ein Problem, mit diesen Werten zu arbeiten, da der Fehler ziemlich fahrlässig ist, aber es ist mühsam, dies dem Benutzer anzuzeigen.Eine mögliche Lösung hierfür wäre die Verwendung einer benutzerdefinierten Implementierung des Gleitkomma-Datentyps wie
BigDecimal
. Es werden bessere Berechnungsmechanismen unterstützt, die die Rundungsfehler zumindest nicht maschinenspezifisch, sondern leistungsmäßig langsamer eingrenzen.Wenn Sie eine hohe Produktivität benötigen, sollten Sie sich besser an die einfachen Typen halten. Falls Sie mit wichtigen Finanzdaten arbeiten und jeder Cent wichtig ist (wie eine Forex-Anwendung oder ein Kasinospiel), würde ich Ihnen empfehlen,
Long
oder zu verwendenlong
.Long
Dies würde es Ihnen ermöglichen, große Mengen und eine gute Präzision zu handhaben. Nehmen wir einmal an, Sie brauchen zum Beispiel 4 Stellen nach dem Komma. Sie müssen den Betrag nur mit 10000 multiplizieren. Ich habe Erfahrung in der Entwicklung von Online-Casinospielen und habe gesehenLong
, dass er oft verwendet wird, um das Geld in Cent darzustellen . Bei Forex-Anwendungen ist die Genauigkeit wichtiger, sodass Sie einen größeren Multiplikator benötigen. Ganzzahlen sind jedoch frei von Rundungsproblemen (natürlich sollten Sie die manuelle Rundung wie in 3/2 selbst vornehmen).Eine akzeptable Option wäre die Verwendung der Standardgleitkommatypen -
Float
undDouble
wenn die Leistung wichtiger ist als die Genauigkeit auf Hundertstel des Cent. Anschließend müssen Sie in Ihrer Anzeigelogik lediglich eine vordefinierte Formatierung verwenden , damit der Benutzer nicht von der Hässlichkeit potenzieller Maschinenrundungen beeinträchtigt wird.quelle
Bei Spielen im kleinen Maßstab und wenn die Prozessgeschwindigkeit ein wichtiges Problem ist (aufgrund der Präzision oder der Arbeit mit einem mathematischen Koprozessor kann dies schmerzhaft verlangsamen), reicht das Doppelte aus.
Bei Spielen in großem Maßstab (z. B. Social Games) und wenn die Verarbeitungsgeschwindigkeit nicht begrenzt ist, ist BigDecimal besser. Weil hier,
Ressourcen:
Von https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency
Aus Bloch, J., Effective Java, 2. Aufl., Punkt 48:
Schauen Sie sich auch an
quelle
Sie möchten speichern Sie Ihre Währung in
long
und berechnen Sie Ihre Währung indouble
zumindest als Backup. Sie möchten, dass alle Transaktionen als stattfindenlong
.Der Grund, warum Sie Ihre Währung speichern möchten,
long
ist, dass Sie keine Währung verlieren möchten.Nehmen wir an, Sie verwenden a
double
und haben kein Geld. Jemand gibt Ihnen drei Groschen und nimmt sie dann zurück.Naja, das ist nicht so cool. Vielleicht möchte jemand mit 10 US-Dollar sein Vermögen verschenken, indem er Ihnen zuerst drei Cent und dann 9,70 US-Dollar an einen anderen verschenkt.
Und dann gibst du ihnen die Groschen zurück:
Das ist einfach kaputt.
Lassen Sie uns nun eine lange verwenden, und wir werden Zehntel Cent verfolgen (also 1 = 0,001 $). Geben wir jedem auf dem Planeten eine Milliarde, einhundertzwölf Millionen, fünfundsiebzigtausend, einhundertdreiundvierzig Dollar:
Warten Sie, wir können jedem mehr als eine Milliarde Dollar geben und nur etwas mehr als zwei ausgeben? Überlauf ist hier eine Katastrophe.
Also, wann immer Sie einen Geldbetrag berechnen, um ihn zu übertragen, zu verwenden
double
undMath.round
zu erhaltenlong
. Richten Sie dann Salden ein (addieren und subtrahieren Sie beide Konten), indem Sie verwendenlong
.Ihre Wirtschaft wird nicht auslaufen und auf eine Billiarde Dollar anwachsen.
Es gibt schwierigere Probleme - zum Beispiel, was machen Sie, wenn Sie zwanzig Zahlungen leisten? * - aber dies sollte Ihnen den Einstieg erleichtern.
* Sie berechnen, was eine Zahlung ist, rund um
long
; dann multipliziere mit20.0
und überprüfe, ob es in Reichweite ist; In diesem20L
Fall multiplizieren Sie die Zahlung mit , um den von Ihrem Guthaben abgezogenen Betrag zu erhalten. Im Allgemeinen müssen alle Transaktionen so behandelt werdenlong
, dass Sie wirklich alle einzelnen Transaktionen zusammenfassen müssen. Sie können als Abkürzung multiplizieren, aber Sie müssen sicherstellen, dass Sie keine Rundungsfehler hinzufügen und nicht überlaufen, was bedeutet, dass Sie dies überprüfen müssen,double
bevor Sie die eigentliche Berechnung mit ausführenlong
.quelle
Ich würde sogar sagen, dass jeder Wert, der dem Benutzer angezeigt wird, fast immer eine ganze Zahl sein sollte. Geld ist nur das prominenteste Beispiel dafür. Wenn Sie einem Monster mit 900 HP viermal 225 Schadenspunkte zufügen und feststellen, dass noch 1 HP übrig sind, werden Sie von der Erfahrung ebenso abgezogen wie wenn Sie feststellen, dass Sie einen unsichtbaren Bruchteil eines Pennys sind, dem es an Leistung mangelt.
Auf der technischen Seite ist es meiner Meinung nach erwähnenswert, dass man nicht auf Floats zurückgreifen muss, um fortgeschrittene Dinge wie Interesse zu erledigen. Solange Sie genug Headroom in Ihrem gewählten Integer-Typ haben, wird eine Multiplikation und Division für eine Multiplikation mit einer Dezimalzahl stehen, zum Beispiel um 4% zu addieren, abgerundet:
4% addieren, gerundet durch Standardkonventionen:
Hier gibt es keine Fließkommaungenauigkeit, die Rundung ist immer exakt auf die
.5
Marke aufgeteilt.Edit, die eigentliche Frage:
Ich fange an zu denken , zu sehen , wie die Debatte gegangen ist , dass umreißt , was die Frage dreht sich alles um kann nützlicher sein als eine einfache
int
/float
Antwort. Im Kern der Frage geht es nicht um Datentypen, sondern darum, die Kontrolle über die Details eines Programms zu übernehmen.Die Verwendung einer Ganzzahl zur Darstellung eines nicht ganzzahligen Werts zwingt den Programmierer, sich mit den Implementierungsdetails zu befassen. "Welche Präzision soll verwendet werden?" und "Welche Art zu runden?" sind Fragen, die explizit beantwortet werden müssen.
Ein Float dagegen zwingt den Programmierer nicht zur Sorge, er macht schon so ziemlich das, was man erwarten würde. Da ein Gleitkomma jedoch nicht unendlich genau ist, findet eine gewisse Rundung statt, und diese Rundung ist ziemlich unvorhersehbar.
Was passiert bei One Use Floats und möchten die Rundung steuern? Es erweist sich als fast unmöglich. Die einzige Möglichkeit, ein Float wirklich vorhersehbar zu machen, besteht darin, nur Werte zu verwenden, die in ganzen
2^n
s dargestellt werden können. Aber diese Konstruktion macht die Verwendung von Schwimmern ziemlich schwierig.Die Antwort auf die einfache Frage lautet also: Wenn Sie die Kontrolle übernehmen möchten, verwenden Sie Ganzzahlen, wenn nicht, verwenden Sie Gleitkommazahlen.
Aber die Frage, die diskutiert wird, ist nur eine andere Form der Frage: Wollen Sie die Kontrolle übernehmen?
quelle
Auch wenn es "nur ein Spiel" ist, würde ich Money Pattern von Martin Fowler verwenden, unterstützt durch eine lange.
Warum?
Lokalisierung (L10n): Mit diesem Muster können Sie Ihre Spielwährung leicht lokalisieren. Denken Sie an die alten Tycoon-Spiele wie "Transport Tycoon". Sie ermöglichen dem Spieler auf einfache Weise, die Währung im Spiel (dh von Britischem Pfund zu US-Dollar) zu ändern, um die Währung der realen Welt zu erfüllen.
Und
Das heißt, Sie können 9.000-mal den aktuellen M2 US-Geldvorrat (~ 10.000 Milliarden Dollar) speichern . Geben Sie genügend Platz andere Weltwährung zu verwenden, wahrscheinlich, auch diejenigen , die hatte / hat Überblähung (Wenn neugierig, sehen nach dem Ersten Weltkrieg deutsche Inflation , wobei 1 Pfund Brot 3000000000 Mark war)
Lange ist leicht zu bestehen, sehr schnell und sollen Ihnen genug Platz gibt alle Interessen Berechnungen mit nur Integer - Arithmetik zu tun, gibt E - Business - Antwort eine Erklärung darüber , wie das zu tun.
quelle
Wie viel Arbeit möchten Sie dafür investieren? Wie wichtig ist Genauigkeit? Interessieren Sie sich für die Nachverfolgung der Bruchfehler, die bei der Rundung auftreten, und für die Ungenauigkeit der Darstellung von Dezimalzahlen in einem Binärsystem?
Letztendlich tendiere ich dazu, etwas mehr Zeit für das Codieren und Implementieren von Komponententests für "Eckfälle" und bekannte problematische Fälle aufzuwenden - daher würde ich in Form einer modifizierten BigInteger-Zahl denken, die beliebig große Mengen umfasst und die gebrochenen Bits entweder mit einem BigDecimal oder verwaltet ein BigRational-Teil (zwei BigInteger, einer für den Nenner und einer für den Zähler) - und Code einschließen, um den Bruchteil als tatsächlichen Bruch zu erhalten, indem (möglicherweise nur in regelmäßigen Abständen) ein nicht-gebrochener Teil zur Haupt-BigInteger hinzugefügt wird. Dann würde ich intern alles in Cent verfolgen, nur um die Bruchteile aus den GUI-Berechnungen herauszuhalten.
Wahrscheinlich viel zu komplex für ein (einfaches) Spiel - aber gut für eine Bibliothek, die als Open Source veröffentlicht wurde! Müsste nur Wege finden, um die Leistung im Umgang mit den Bruchteilen gut zu halten ...
quelle
Mit Sicherheit nicht schweben. Mit nur 7 Stellen haben Sie nur Genauigkeit wie:
12.345,67 123.456,7x
Sie sehen also, bereits mit 10 ^ 5 Dollar verlieren Sie den Überblick über Pennys. double ist für Spielzwecke verwendbar und aus Genauigkeitsgründen nicht im wirklichen Leben.
Aber long (und damit meine ich 64-Bit oder long long in C ++) reicht nicht aus, um Transaktionen zu verfolgen und zusammenzufassen. Es reicht aus, den Nettobestand eines Unternehmens zu halten, aber alle Transaktionen eines Jahres könnten überlaufen. Das hängt davon ab, wie groß Ihre finanzielle "Welt" im Spiel ist.
quelle
Eine andere Lösung, die ich hier hinzufügen werde, ist, eine Geldklasse zu machen. Die Klasse wäre so etwas wie (könnte sogar einfach eine Struktur sein).
Auf diese Weise können Sie die Cent in diesem Fall als Ganzzahl und den gesamten Dollar als Ganzzahl darstellen. Da Sie ein separates Flag haben, um negativ zu sein, können Sie vorzeichenlose Ganzzahlen für Ihre Werte verwenden, die den möglichen Betrag verdoppeln (Sie können auch eine Zahlenklasse schreiben, die diese Idee auf Zahlen anwendet, um WIRKLICH große Zahlen zu erhalten). Alles, was Sie tun müssten, ist eine Überladung der mathematischen Operatoren, und Sie könnten dies wie jeden alten Datentyp verwenden. Es nimmt mehr Speicherplatz in Anspruch, erweitert aber die Grenzwerte.
Dies könnte weiter ausgebaut werden, um beispielsweise die Anzahl der Teileinheiten pro ganze Einheit einzuschließen, sodass Sie Währungen haben können, die sich auf etwas anderes als 100 Teileinheiten, in diesem Fall Cent, pro Dollar, aufteilen. Sie könnten beispielsweise 125 Floopies zu einem Flooper zusammensetzen.
Bearbeiten:
Ich werde die Idee dahingehend erweitern, dass Sie auch eine Art Nachschlagetabelle anzeigen können, auf die die Geldklasse zugreifen kann, um einen Wechselkurs für ihre Währung gegenüber allen anderen Währungen zu erhalten. Sie können dies in Ihre Operatorüberladungsfunktionen integrieren. Wenn Sie also automatisch versuchen, 4 US-Dollar zu 4 britischen Pfund hinzuzufügen, erhalten Sie automatisch 6,6 Pfund (zum Zeitpunkt des Schreibens).
quelle
Wie in einem der Kommentare zu Ihrer ursprünglichen Frage erwähnt, wurde dieses Problem in StackExchange behandelt: https://stackoverflow.com/questions/8148684/what-is-the-best-data-type-to-use-for- Geld-in-Java-App
Grundsätzlich sollten Gleitkommatypen niemals zur Darstellung von Währungen verwendet werden, und Java hat wie die meisten Sprachen einen angemesseneren Typ. In der Oracle-eigenen Dokumentation zu Primitiven wird die Verwendung von BigDecimal empfohlen .
quelle
Verwenden Sie Klasse, es ist der beste Weg. Sie können die Ausführung Ihres Geldes verbergen, Sie können jederzeit doppelt / lang wechseln, was immer Sie wollen.
Beispiel
In Ihrem Code verwenden Sie einfach Getter, es ist meiner Meinung nach besser und Sie können nützlichere Methoden speichern.
quelle