Wann sollte ich double anstelle von decimal verwenden?

265

Ich kann drei Vorteile für die Verwendung double(oder float) anstelle von nennen decimal:

  1. Verbraucht weniger Speicher.
  2. Schneller, da Gleitkomma-Mathematikoperationen von Prozessoren nativ unterstützt werden.
  3. Kann einen größeren Zahlenbereich darstellen.

Diese Vorteile scheinen jedoch nur für rechenintensive Operationen zu gelten, wie sie beispielsweise in Modellierungssoftware zu finden sind. Natürlich sollten Doppelbilder nicht verwendet werden, wenn Präzision erforderlich ist, z. B. bei Finanzberechnungen. Gibt es also praktische Gründe, sich jemals für double(oder float) anstatt für decimal"normale" Anwendungen zu entscheiden?

Bearbeitet, um hinzuzufügen: Danke für all die tollen Antworten, die ich von ihnen gelernt habe.

Eine weitere Frage: Einige Leute haben darauf hingewiesen, dass Doppelte reelle Zahlen genauer darstellen können. Wenn erklärt, würde ich denken, dass sie sie normalerweise auch genauer darstellen. Aber ist es eine wahre Aussage, dass die Genauigkeit (manchmal erheblich) abnehmen kann, wenn Gleitkommaoperationen ausgeführt werden?

Jamie Ide
quelle
Siehe auch stackoverflow.com/questions/2545567/…
Ian Ringrose
5
Dies wird ziemlich regelmäßig positiv bewertet und ich habe immer noch Probleme damit. Zum Beispiel arbeite ich an einer Anwendung, die Finanzberechnungen durchführt, sodass ich durchgehend Dezimalzahlen verwende. Aber die Funktionen Math und VisualBasic.Financial verwenden double, so dass es viele Konvertierungen gibt, bei denen ich ständig die Verwendung von Dezimalstellen errate.
Jamie Ide
@JamieIde das ist verrückt die Finanzfunktionen verwenden double, Geld sollte immer in Dezimalzahl sein.
Chris Marisic
@ChrisMarisic Aber was kann Jamie Ide tun, wenn er mit Double mit Legacy-Mist arbeitet? Dann sollten Sie auch double verwenden, sonst verursachen die vielen Konvertierungen Rundungsfehler ... keine Wunder, dass er VisualBasic pfffhh erwähnt hat .....
Elisabeth
@ Elizabeth Ich würde wahrscheinlich eine andere Bibliothek verwenden, die Dezimalstellen richtig unterstützt. Was auch immer VisualBasic.Financial bietet, es gibt es heute wahrscheinlich in mehreren anderen Bibliotheken
Chris Marisic,

Antworten:

306

Ich denke, Sie haben die Vorteile recht gut zusammengefasst. Ihnen fehlt jedoch ein Punkt. Der decimalTyp ist nur bei der Darstellung von Basis-10- Zahlen genauer (z. B. diejenigen, die in Währungs- / Finanzberechnungen verwendet werden). Im Allgemeinen bietet der doubleTyp mindestens die gleiche Präzision (jemand korrigiert mich, wenn ich falsch liege) und definitiv eine höhere Geschwindigkeit für beliebige reelle Zahlen. Die einfache Schlussfolgerung lautet: Wenn Sie überlegen, welche doubleSie verwenden möchten, verwenden Sie sie immer, es sei denn, Sie benötigen die base 10Genauigkeit, die sie decimalbietet.

Bearbeiten:

In Bezug auf Ihre zusätzliche Frage zur Verringerung der Genauigkeit von Gleitkommazahlen nach Operationen ist dies ein etwas subtileres Problem. Tatsächlich nimmt die Präzision (ich verwende den Begriff hier austauschbar für Genauigkeit) nach jeder Operation stetig ab. Dies hat zwei Gründe:

  1. die Tatsache, dass bestimmte Zahlen (am offensichtlichsten Dezimalstellen) nicht wirklich in Gleitkommaform dargestellt werden können
  2. Rundungsfehler treten auf, als würden Sie die Berechnung von Hand durchführen. Es hängt stark vom Kontext ab (wie viele Vorgänge Sie ausführen), ob diese Fehler signifikant genug sind, um viel Nachdenken zu rechtfertigen.

Wenn Sie zwei Gleitkommazahlen vergleichen möchten, die theoretisch äquivalent sein sollten (aber mit unterschiedlichen Berechnungen ermittelt wurden), müssen Sie in allen Fällen einen bestimmten Toleranzgrad zulassen (wie stark variiert, ist aber normalerweise sehr klein). .

Eine detailliertere Übersicht über die besonderen Fälle, in denen Genauigkeitsfehler auftreten können, finden Sie im Abschnitt Genauigkeit des Wikipedia-Artikels . Wenn Sie eine ausführliche (und mathematische) Diskussion über Gleitkommazahlen / -operationen auf Maschinenebene wünschen, lesen Sie den oft zitierten Artikel Was jeder Informatiker über Gleitkomma-Arithmetik wissen sollte .

Noldorin
quelle
1
Können Sie ein Beispiel e einer Basis-10-Zahl angeben, mit dem die Genauigkeit bei der Konvertierung in Basis 2 verloren geht?
Mark Cidade
@Mark: 1.000001 ist ein Beispiel, zumindest laut Jon Skeet. (Siehe Frage 3 dieser Seite: yoda.arachsys.com/csharp/teasers-answers.html )
Noldorin
25
@Mark: sehr einfaches Beispiel: 0.1 ist ein periodischer Bruch in Basis 2, daher kann er nicht genau in a ausgedrückt werden double. Moderne Computer drucken immer noch den richtigen Wert, aber nur, weil sie das Ergebnis „erraten“ - nicht, weil es wirklich richtig ausgedrückt wird.
Konrad Rudolph
1
Der DecimalTyp hat eine Genauigkeit von 93 Bit in der Mantisse, verglichen mit etwa 52 Bit für double. Ich wünschte, Microsoft würde das IEEE 80-Bit-Format unterstützen, auch wenn es auf 16 Byte aufgefüllt werden müsste. es hätte eine größere Reichweite als doubleoder eine Decimalviel bessere Geschwindigkeit als die DecimalUnterstützung für transzendentale Operationen (z. B. sin (x), log (x) usw.) und Präzision ermöglicht, die zwar nicht ganz so gut sind, wie Decimales viel besser wäre als double.
Supercat
@charlotte: Wenn du meinen vollständigen Beitrag liest, wirst du sehen, dass das erklärt ist.
Noldorin
59

Sie scheinen die Vorteile der Verwendung eines Gleitkommatyps genau zu kennen. Ich neige in allen Fällen dazu, für Dezimalstellen zu entwerfen, und verlasse mich auf einen Profiler, der mich darüber informiert, ob Operationen mit Dezimalstellen Engpässe oder Verlangsamungen verursachen. In diesen Fällen werde ich "down cast", um zu verdoppeln oder zu schweben, aber nur intern, und sorgfältig versuchen, den Präzisionsverlust zu verwalten, indem ich die Anzahl der signifikanten Stellen in der ausgeführten mathematischen Operation beschränke.

Wenn Ihr Wert vorübergehend ist (nicht wiederverwendet wird), können Sie im Allgemeinen einen Gleitkommatyp verwenden. Das eigentliche Problem bei Gleitkommatypen sind die folgenden drei Szenarien.

  1. Sie aggregieren Gleitkommawerte (in diesem Fall setzen sich die Genauigkeitsfehler zusammen).
  2. Sie erstellen Werte basierend auf dem Gleitkommawert (z. B. in einem rekursiven Algorithmus).
  3. Sie rechnen mit einer sehr großen Anzahl von signifikanten Stellen (zum Beispiel 123456789.1 * .000000000000000987654321)

BEARBEITEN

Gemäß der Referenzdokumentation zu C # -Dezimalzahlen :

Das Dezimalschlüsselwort bezeichnet einen 128-Bit-Datentyp. Im Vergleich zu Gleitkommatypen weist der Dezimaltyp eine größere Genauigkeit und einen kleineren Bereich auf, wodurch er für finanzielle und monetäre Berechnungen geeignet ist.

Um meine obige Aussage zu verdeutlichen:

Ich neige in allen Fällen dazu, für Dezimalstellen zu entwerfen, und verlasse mich auf einen Profiler, der mich darüber informiert, ob Operationen mit Dezimalstellen Engpässe oder Verlangsamungen verursachen.

Ich habe immer nur in Branchen gearbeitet, in denen Dezimalstellen günstig sind. Wenn Sie an Phsyics- oder Grafik-Engines arbeiten, ist es wahrscheinlich viel vorteilhafter, für einen Gleitkommatyp (Float oder Double) zu entwerfen.

Dezimal ist nicht unendlich genau (es ist unmöglich, unendliche Genauigkeit für Nicht-Integral in einem primitiven Datentyp darzustellen), aber es ist weitaus genauer als doppelt:

  • Dezimal = 28-29 signifikante Stellen
  • double = 15-16 signifikante Stellen
  • float = 7 signifikante Stellen

BEARBEITEN 2

Als Antwort auf den Kommentar von Konrad Rudolph ist Punkt 1 (oben) definitiv richtig. Die Aggregation von Ungenauigkeiten verstärkt sich tatsächlich. Ein Beispiel finden Sie im folgenden Code:

private const float THREE_FIFTHS = 3f / 5f;
private const int ONE_MILLION = 1000000;

public static void Main(string[] args)
{
    Console.WriteLine("Three Fifths: {0}", THREE_FIFTHS.ToString("F10"));
    float asSingle = 0f;
    double asDouble = 0d;
    decimal asDecimal = 0M;

    for (int i = 0; i < ONE_MILLION; i++)
    {
        asSingle += THREE_FIFTHS;
        asDouble += THREE_FIFTHS;
        asDecimal += (decimal) THREE_FIFTHS;
    }
    Console.WriteLine("Six Hundred Thousand: {0:F10}", THREE_FIFTHS * ONE_MILLION);
    Console.WriteLine("Single: {0}", asSingle.ToString("F10"));
    Console.WriteLine("Double: {0}", asDouble.ToString("F10"));
    Console.WriteLine("Decimal: {0}", asDecimal.ToString("F10"));
    Console.ReadLine();
}

Dies gibt Folgendes aus:

Three Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 599999.9999886850
Decimal: 600000.0000000000

Wie Sie sehen können, sind die Ergebnisse des Doppelten weniger genau (obwohl sie wahrscheinlich korrekt gerundet werden), obwohl wir aus derselben Quellenkonstante addieren, und der Float ist weitaus ungenauer, bis zu dem Punkt, an dem er nur auf reduziert wurde zwei signifikante Ziffern.

Michael Meadows
quelle
1
Punkt 1 ist falsch. Präzisions- / Rundungsfehler treten nur beim Gießen auf, nicht bei Berechnungen. Es ist natürlich richtig, dass die meisten mathematischen Operationen instabil sind, wodurch der Fehler multipliziert wird. Dies ist jedoch ein weiteres Problem, das für alle Datentypen mit begrenzter Genauigkeit gilt, insbesondere für Dezimalstellen.
Konrad Rudolph
1
@Konrad Rudolph, siehe das Beispiel in "EDIT 2" als Beweis für den Punkt, den ich in Punkt 1 ansprechen wollte. Oft manifestiert sich dieses Problem nicht, weil die positive Ungenauigkeit mit der negativen Ungenauigkeit in Einklang steht und sie sich einwaschen Das Aggregat, aber das Aggregieren derselben Zahl (wie im Beispiel), hebt das Problem hervor.
Michael Meadows
Tolles Beispiel. Ich habe es gerade meinen Nachwuchsentwicklern gezeigt und die Kinder waren begeistert.
Machado
Jetzt können Sie dasselbe mit 2/3 statt 3/5 machen ... Sie sollten etwas über das sexagesimale Zahlensystem lernen, das 2/3 perfekt handhabt.
Gnasher729
1
@ gnasher729, die Verwendung von 2/3 anstelle von 3/5 wurde für die verschiedenen Typen nicht perfekt gehandhabt . Interessanterweise ergab der Float-Wert, Single: 667660.400000000000während der Dezimalwert ergab Decimal: 666666.7000000000. Der Float-Wert liegt etwas unter tausend über dem korrekten Wert.
Jhenninger
25

Verwenden Sie Dezimalzahlen für Basis-10-Werte, z. B. Finanzberechnungen, wie andere vorgeschlagen haben.

Für beliebig berechnete Werte ist double im Allgemeinen genauer.

Wenn Sie beispielsweise das Gewicht jeder Zeile in einem Portfolio berechnen möchten, verwenden Sie double, da das Ergebnis nahezu 100% ergibt.

Im folgenden Beispiel liegt doubleResult näher an 1 als decimalResult:

// Add one third + one third + one third with decimal
decimal decimalValue = 1M / 3M;
decimal decimalResult = decimalValue + decimalValue + decimalValue;
// Add one third + one third + one third with double
double doubleValue = 1D / 3D;
double doubleResult = doubleValue + doubleValue + doubleValue;

Nehmen wir also noch einmal das Beispiel eines Portfolios:

  • Der Marktwert jeder Zeile im Portfolio ist ein Geldwert und wird wahrscheinlich am besten als Dezimalzahl dargestellt.

  • Das Gewicht jeder Zeile im Portfolio (= Marktwert / SUMME (Marktwert)) wird normalerweise besser als doppelt dargestellt.

Joe
quelle
6

Verwenden Sie ein Double oder einen Float, wenn Sie keine Präzision benötigen. In einem von mir geschriebenen Platformer-Spiel habe ich beispielsweise einen Float verwendet, um die Geschwindigkeiten der Spieler zu speichern. Offensichtlich brauche ich hier keine Superpräzision, weil ich schließlich zu einem Int runde, um auf dem Bildschirm zu zeichnen.

FlySwat
quelle
3
Präzision ist der EINZIGE Vorteil von Dezimalstellen, das ist richtig. Sie sollten nicht fragen, wann Sie Gleitkommazahlen über Dezimalstellen verwenden sollen. Das sollte dein erster Gedanke sein. Die Frage ist dann, wann Sie Dezimalstellen verwenden sollten (und die Antwort ist genau hier ... wenn Präzision wichtig ist).
Instance Hunter
3
@ Daniel Straight, es ist lustig, aber ich bin der gegenteiligen Meinung. Ich denke, die Verwendung eines weniger präzisen Typs aufgrund seiner Leistungsmerkmale ist eine Voroptimierung. Möglicherweise müssen Sie für diese Voroptimierung ein Vielfaches bezahlen, bevor Sie den Nutzen erkennen.
Michael Meadows
3
@ Michael Meadows, ich kann dieses Argument verstehen. Zu beachten ist jedoch, dass eine der Hauptbeschwerden bei vorzeitiger Optimierung darin besteht, dass Programmierer nicht wissen, was langsam sein wird. Wir wissen jedoch ohne Zweifel, dass Dezimalstellen langsamer als Doppel sind. Trotzdem denke ich, dass in den meisten Fällen die Leistungsverbesserung für den Benutzer sowieso nicht spürbar ist. In den meisten Fällen wird die Präzision natürlich auch nicht benötigt. Heh.
Instanz Hunter
Dezimaler Gleitkomma ist tatsächlich WENIGER genau als binärer Gleitkomma unter Verwendung der gleichen Anzahl von Bits. Der Vorteil von Decimal besteht darin, dass in der Finanzberechnung häufig DECIMAL-Brüche wie 0,01 dargestellt werden können.
dan04
Nun, das ist nicht ganz richtig :) - In vielen Spielen können Gleitkommazahlen unerwünscht sein, da sie nicht konsistent sind. Siehe hier
BlueRaja - Danny Pflughoeft
4

Berücksichtigen Sie in einigen Buchhaltungsverfahren die Möglichkeit, stattdessen oder in Verbindung integrale Typen zu verwenden. Angenommen, die Regeln, nach denen Sie arbeiten, erfordern, dass jedes Berechnungsergebnis mit mindestens 6 Dezimalstellen übertragen wird und das Endergebnis auf den nächsten Cent gerundet wird.

Eine Berechnung von 1/6 von 100 US-Dollar ergibt 16,666666666666666 ..., sodass der in einem Arbeitsblatt angegebene Wert 16,6666667 US-Dollar beträgt. Sowohl Doppel- als auch Dezimalstellen sollten dieses Ergebnis auf 6 Dezimalstellen genau ergeben. Wir können jedoch jeden kumulativen Fehler vermeiden, indem wir das Ergebnis als Ganzzahl 16666667 übertragen. Jede nachfolgende Berechnung kann mit derselben Genauigkeit durchgeführt und auf ähnliche Weise übertragen werden. Wenn ich das Beispiel fortsetze, berechne ich die Umsatzsteuer in Texas auf diesen Betrag (16666667 * .0825 = 1375000). Addieren der beiden (es ist ein kurzes Arbeitsblatt) 1666667 + 1375000 = 18041667. Wenn Sie den Dezimalpunkt wieder nach innen verschieben, erhalten Sie 18.041667 oder $ 18.04.

Während dieses kurze Beispiel keinen kumulativen Fehler mit Doppel- oder Dezimalzahlen ergeben würde, ist es ziemlich einfach, Fälle zu zeigen, in denen das einfache Berechnen der Doppel- oder Dezimalzahl und das Weitertragen einen signifikanten Fehler akkumulieren würden. Wenn für die Regeln, nach denen Sie arbeiten, eine begrenzte Anzahl von Dezimalstellen erforderlich ist, speichern Sie jeden Wert als Ganzzahl, indem Sie mit 10 ^ (erforderliche Anzahl von Dezimalstellen) multiplizieren und dann durch 10 ^ (erforderliche Anzahl von Dezimalstellen) dividieren, um die tatsächliche zu erhalten Wert vermeidet kumulative Fehler.

In Situationen, in denen Bruchteile von Pennys nicht vorkommen (z. B. ein Verkaufsautomat), gibt es keinen Grund, überhaupt nicht integrale Typen zu verwenden. Stellen Sie sich das einfach so vor, als würden Sie ein paar Cent zählen, nicht Dollar. Ich habe Code gesehen, bei dem jede Berechnung nur ganze Pennys umfasste, aber die Verwendung von Double zu Fehlern führte! Nur ganzzahlige Mathematik hat das Problem behoben. Meine unkonventionelle Antwort lautet also, wenn möglich, auf Doppel- und Dezimalzahlen zu verzichten.

G DeMasters
quelle
3

Wenn Sie binäres Interrop mit anderen Sprachen oder Plattformen benötigen, müssen Sie möglicherweise float oder double verwenden, die standardisiert sind.

Will Dean
quelle
2

Hinweis: Dieser Beitrag basiert auf Informationen zu den Funktionen des Dezimaltyps von http://csharpindepth.com/Articles/General/Decimal.aspx und meiner eigenen Interpretation dessen, was dies bedeutet. Ich gehe davon aus, dass Double eine normale IEEE-Doppelgenauigkeit ist.

Anmerkung 2: Kleinste und größte in diesem Beitrag beziehen sich auf die Größe der Zahl.

Vorteile von "dezimal".

  • "dezimal" kann genau Zahlen darstellen, die als (ausreichend kurze) Dezimalbrüche geschrieben werden können, doppelt nicht. Dies ist wichtig in Finanzbüchern und ähnlichem, wo es wichtig ist, dass die Ergebnisse genau dem entsprechen, was ein Mensch, der die Berechnungen durchführt, geben würde.
  • "dezimal" hat eine viel größere Mantisse als "doppelt". Das bedeutet, dass für Werte innerhalb des normalisierten Bereichs "Dezimal" eine viel höhere Genauigkeit als doppelt hat.

Nachteile der Dezimalstelle

  • Es wird viel langsamer sein (ich habe keine Benchmarks, aber ich würde mindestens eine Größenordnung vielleicht mehr schätzen), Dezimalstellen profitieren nicht von einer Hardwarebeschleunigung und die Arithmetik erfordert eine relativ teure Multiplikation / Division durch Potenzen von 10 ( Dies ist weitaus teurer als Multiplikation und Division durch Potenzen von 2), um den Exponenten vor der Addition / Subtraktion anzupassen und den Exponenten nach der Multiplikation / Division wieder in Reichweite zu bringen.
  • Die Dezimalstelle läuft früher über als die doppelte. Dezimal kann nur Zahlen bis zu ± 2 96 -1 darstellen. Im Vergleich kann double Zahlen bis fast ± 2 1024 darstellen
  • Die Dezimalstelle läuft früher unter. Die kleinsten in Dezimalzahlen darstellbaren Zahlen sind ± 10 -28 . Im Vergleich kann double Werte bis zu 2 -149 (ca. 10 -45 ) darstellen, wenn subnromale Zahlen unterstützt werden, und 2 -126 (ca. 10 -38 ), wenn dies nicht der Fall ist .
  • Dezimalstellen belegen doppelt so viel Speicher wie doppelt.

Meiner Meinung nach sollten Sie standardmäßig "Dezimal" für Geldarbeit und andere Fälle verwenden, in denen eine genaue Übereinstimmung der menschlichen Berechnung wichtig ist, und Sie sollten für den Rest der Zeit das Doppelte als Standardauswahl verwenden.

Plugwash
quelle
2

Kommt darauf an, wofür du es brauchst.

Da float und double binäre Datentypen sind, gibt es in Rundenzahlen einige Schwierigkeiten und Fehler, sodass double beispielsweise 0,1 auf 0,100000001490116 rundet und double auch 1/3 auf 0,33333334326441 rundet. Einfach gesagt, nicht alle reellen Zahlen haben eine genaue Darstellung in doppelten Typen

Glücklicherweise unterstützt C # auch die sogenannte Dezimal-Gleitkomma-Arithmetik, bei der Zahlen eher über das Dezimalzahlensystem als über das Binärsystem dargestellt werden. Somit verliert die dezimale Gleitkomma-Arithmetik beim Speichern und Verarbeiten von Gleitkommazahlen nicht an Genauigkeit . Dies macht es immens geeignet für Berechnungen, bei denen ein hohes Maß an Genauigkeit erforderlich ist.

Neil Meyer
quelle
0

Verwenden Sie Gleitkommawerte, wenn Sie Leistung über Korrektheit bewerten.

Mark Brackett
quelle
6
Dezimalzahlen sind nicht korrekter, außer in bestimmten begrenzten Fällen, die manchmal (keineswegs immer) wichtig sind.
David Thornley
0

Wählen Sie den Funktionstyp Ihrer Anwendung. Wenn Sie Präzision wie bei der Finanzanalyse benötigen, haben Sie Ihre Frage beantwortet. Aber wenn Ihre Bewerbung mit einer Schätzung abrechnen kann, ist Ihr OK mit Double.

Benötigt Ihre Bewerbung eine schnelle Berechnung oder hat er weltweit die Zeit, Ihnen eine Antwort zu geben? Es hängt wirklich von der Art der Anwendung ab.

Grafik hungrig? float oder double ist genug. Finanzdatenanalyse, Meteor trifft einen Planeten mit Präzision? Die würden ein bisschen Präzision brauchen :)

Khan
quelle
8
Dezimalzahlen sind ebenfalls Schätzungen. Sie entsprechen den Konventionen der Finanzarithmetik, aber Berechnungen, die beispielsweise die Physik betreffen, haben keinen Vorteil.
David Thornley
0

Dezimal hat breitere Bytes, double wird von der CPU nativ unterstützt. Dezimal ist Basis-10, daher findet eine Dezimal-zu-Doppel-Konvertierung statt, während eine Dezimalzahl berechnet wird.

For accounting - decimal
For finance - double
For heavy computation - double

Beachten Sie, dass .NET CLR nur Math.Pow (double, double) unterstützt. Dezimal wird nicht unterstützt.

.NET Framework 4

[SecuritySafeCritical]
public static extern double Pow(double x, double y);
Jeson Martajaya
quelle
0

Ein Doppelwert wird standardmäßig in die wissenschaftliche Notation serialisiert, wenn diese Notation kürzer als die Dezimalanzeige ist. (z. B. .00000003 ist 3e-8) Dezimalwerte werden niemals in wissenschaftliche Notation serialisiert. Bei der Serialisierung für den Verbrauch durch eine externe Partei kann dies eine Überlegung sein.

chris klassen
quelle