Django: FloatField oder DecimalField für Währung?

76

Ich bin gespannt, welches als Währungsfeld besser geeignet wäre. Ich werde einfache Operationen durchführen, z. B. die Differenzierung, den Prozentsatz zwischen alten und neuen Preisen. Ich habe vor, zwei Ziffern nach der Null (dh 10,50) zu behalten. Wenn diese Ziffern meistens Null sind, verstecke ich diese Zahlen und zeige sie als "10" an.

ps: Währung basiert NICHT auf Dollar :)

Hellnar
quelle

Antworten:

129

Immer DecimalFieldfür Geld verwenden. Selbst einfache Operationen (Addition, Subtraktion) sind nicht immun gegen Float-Rundungsprobleme:

>>> 10.50 - 0.20
10.300000000000001

>>> Decimal('10.50') - Decimal('0.20')
Decimal('10.30')
Seth
quelle
7
>>> 10.50 - 0.20in Python 2.7 bekommen 10.3.
Rockallite
6
Welp, Mist. Ich werde eine Menge Daten aus meinem FloatField-Feld / meinen FloatField-Spalten nach DecimalField migrieren ...
teewuane
Höchste Zeit, wir haben ein "Verwenden Sie kein Gleitkomma für Geld" analog zu Bobince's stackoverflow.com/a/1732454/604511
Jesvin Jose
@Rockallite - versuchen Sie es 1.00 + 0.14oder "%.15f" % (10.5 - 0.2)- Das Problem kann durch (ich nehme an) bugs.python.org/issue1580 oder ähnliches verdeckt werden .
Seth
@teewuane Haben Sie Probleme beim Umstellen von FloatField auf DecimalField in Ihrem Django-Projekt festgestellt? Ich überarbeite einige alte Projekte, die mit Geld umgehen, benutze aber Float überall, OMG, Thx im Voraus.
Menglong Li
57

Die Antwort auf die Frage ist richtig, jedoch werden einige Benutzer über diese Frage stolpern, um den Unterschied zwischen DecimalField und FloatField herauszufinden. Das von Seth angesprochene Float-Rounding-Problem ist ein Problem für die Währung.

Die Django Docs Staaten

Die FloatField-Klasse wird manchmal mit der DecimalField-Klasse verwechselt. Obwohl beide reelle Zahlen darstellen, repräsentieren sie diese Zahlen unterschiedlich. FloatField verwendet intern den Float-Typ von Python, während DecimalField den Decimal-Typ von Python verwendet. Lesen Sie hier mehr .

Hier sind weitere Unterschiede zwischen den beiden Feldern:

DecimalField:

  • DecimalFields muss ein Attribut 'decimal_places' und ein Attribut 'max_digits' definieren.
  • Sie erhalten zwei Freiformvalidierungen, die hier enthalten sind, aus den oben genannten erforderlichen Attributen. Wenn Sie also max_digits auf 4 setzen und eine Dezimalzahl von 4,00000 (5 Stellen) eingeben, wird folgende Fehlermeldung angezeigt: Stellen Sie sicher, dass nicht mehr als 4 vorhanden sind Ziffern insgesamt.
  • Sie erhalten auch eine ähnliche Formularüberprüfung für Dezimalstellen (die in den meisten Browsern auch am Frontend mithilfe des step-Attributs im Eingabefeld überprüft wird. Wenn Sie decimal_places = 1 festlegen und 0,001 als Wert eingeben, wird eine Fehlermeldung angezeigt dass der Mindestwert 0,1 sein muss.
  • Gibt eine Dezimalzahl zurück. Dezimal, Typ ist <class 'decimal.Decimal'>
  • Hat keine zusätzliche Validierung als DecimalField
  • Bei einem Dezimaltyp wird die Rundung aufgrund der erforderlichen Attribute, die wie oben beschrieben festgelegt werden müssen, auch für Sie behandelt. Also aus der Shell, wenn Sie
  • In der Datenbank (postgresql) wird das DecimalField als numerischer Typ (max_digits, decimal_laces) gespeichert und der Speicher als "main" festgelegt. Im obigen Beispiel ist der Typ numerisch (4,1).

Mehr zu DecimalField aus den Django Docs .

FloatField:

  • Gibt den eingebauten Float-Typ zurück. <type 'float'>
  • Keine intelligente Rundung und kann tatsächlich zu Rundungsproblemen führen, wie in Seths Antwort beschrieben.
  • Verfügt nicht über die zusätzliche Formularüberprüfung, die Sie von DecimalField erhalten
  • In der Datenbank (postgresql) wird das FloatField als Typ mit "doppelter Genauigkeit" gespeichert und der Speicher als "normal" festgelegt.

Mehr zu FloatField aus den Django Docs .

Gilt für beide:

  • Beide Felder erstrecken sich von der Klasse "Field" und können "blank", "null", "verbose_name", "name", "primary_key", "max_length", "unique", "db_index", "rel", "default" akzeptieren ',' editierbar ',' serialisieren ',' unique_for_date ',' unique_for_month ',' unique_for_year ',' selection ',' help_text ',' db_column ',' db_tablespace ',' auto_created ',' validators ',' error_messages ' , wie alle Felder, die sich von "Feld" erstrecken, haben würden.
  • Das Standard-Formular-Widget für beide Felder ist ein TextInput.

Ich bin auf diese Frage gestoßen, als ich nach dem Unterschied zwischen den beiden Feldern gesucht habe, also denke ich, dass dies denjenigen in der gleichen Situation helfen wird :)

UPDATE: Um die Frage zu beantworten, denke ich, dass Sie mit beiden davonkommen können, um die Währung darzustellen, obwohl Dezimal viel besser passt. Es gibt ein Rundungsproblem, wenn es um Gleitkommazahlen geht. Sie müssen es also verwenden round(value, 2), um Ihre Gleitkommadarstellung auf zwei Dezimalstellen gerundet zu halten. Hier ist ein kurzes Beispiel:

>>> round(1.13 * 50 + .01, 2)
56.51

Sie können immer noch Probleme mit Float und Round bekommen. Wie hier sehen wir es auf einen Wert von 5 abrunden:

>>> round(5.685, 2)
5.68

Aber in diesem Fall wird es abrunden:

>>> round(2.995, 2)
3.0

Es hat alles damit zu tun, wie der Float im Speicher gespeichert ist. Siehe hier .

radtek
quelle
Es scheint, als würde ich DecimalField immer über FloatField verwenden. Ich sehe keinen Vorteil / Benutzerfreundlichkeit / Einfachheit bei der Auswahl von FloatField, während ich mich mit den Rundungsproblemen auseinandersetze.
Ankan-Zerob
1
yeah DecimalField, wenn Sie es mit einer endlichen Menge von Dezimalstellen wie mit Währung zu tun haben. Wenn Sie den Float-Typ möchten, verwenden Sie FloatField (wahrscheinlich ein seltenerer Anwendungsfall).
Radtek
8

Ich weiß, dass dies super alt ist, aber ich bin darauf gestoßen und habe nach etwas völlig anderem gesucht, und ich wollte da rauswerfen, dass es im Allgemeinen nicht ratsam ist, Gleitkommazahlen (Gleitkomma- oder Dezimalzahlen) als Währung zu verwenden, wie es die mathematische Gleitkomma-Rundung tun wird führen immer zu kleinen Berechnungsfehlern, die im Laufe der Zeit zu sehr großen Abweichungen führen können.

Verwenden Sie stattdessen ein Ganzzahlfeld oder eine Zeichenfolge nach Ihren Wünschen. Multiplizieren Sie Ihre Währung, um die Dezimalstelle an das Ende zu verschieben, und geben Sie beim Speichern eine ganze Zahl ein. Verschieben Sie diese Dezimalstelle dann wieder dorthin, wo sie hingehört, wenn Sie sie anzeigen müssen. Auf diese Weise behandeln Banken (und die meisten Währungsbibliotheken) Daten und ersparen Ihnen später jede Menge Ärger.

Ich habe das auf die harte Tour gelernt, weil es nicht wirklich ein allgemeines Thema ist. Vielleicht erspart dies jemand anderem, dasselbe zu tun.

Nathan Cox
quelle
7

Bearbeiten: Das Satchmo-Projekt ist nicht mehr aktiv. Sehen Sie sich diese Alternativen für den Umgang mit Währungen an


Das in Django ansässige Satchmo-Projekt verfügt über ein CurrencyField und ein CurrencyWidget, die einen Blick wert sind.

Überprüfen Sie das App-Verzeichnis satchmo_utils auf die Quelle

Sxalexander
quelle