57 Stimmen

Dezimal vs. Doppelte Geschwindigkeit

Ich schreibe Finanzanwendungen, bei denen ich ständig vor der Entscheidung stehe, ob ich ein Double oder eine Dezimalzahl verwenden soll.

Ich rechne immer mit Zahlen, die nicht mehr als 5 Dezimalstellen haben und nicht größer als ~100.000 sind. Ich habe das Gefühl, dass alle diese Zahlen ohne Rundungsfehler als Potenzen dargestellt werden können, aber ich war mir nie sicher.

Ich würde die Umstellung von Dezimalzahlen auf Paschalzahlen wegen des offensichtlichen Geschwindigkeitsvorteils vornehmen, aber letztendlich verwende ich immer noch die ToString-Methode, um die Preise an die Börsen zu übermitteln, und muss sicherstellen, dass sie immer die erwartete Zahl ausgibt. (89,99 statt 89,99000000001)

Fragen:

  1. Ist der Geschwindigkeitsvorteil wirklich so groß, wie naive Tests vermuten lassen? (~100 mal)
  2. Gibt es eine Möglichkeit, die Ausgabe von ToString zu garantieren, was ich will? Ist dies durch die Tatsache gewährleistet, dass meine Zahl immer darstellbar ist?

UPDATE: Ich habe ~ 10 Milliarden Preisaktualisierungen zu verarbeiten, bevor meine App laufen kann, und ich habe mit dezimalen jetzt für die offensichtliche Schutzgründe implementiert, aber es dauert ~ 3 Stunden, nur um einzuschalten, würde das Doppelte meine Einschaltzeit dramatisch reduzieren. Gibt es einen sicheren Weg, um es mit Verdoppelungen zu tun?

1 Stimmen

Was ist Ihr Engpass? Wo genau verbrauchen Sie alle Ihre CPU-Zyklen? Ohne solide Messungen ist die Wahrscheinlichkeit groß, dass Sie sich mit der völlig falschen Sache beschäftigen.

4 Stimmen

Da dezimale Nenner Potenzen von 10 und binäre Nenner Potenzen von 2 sind, kann nicht einmal 1 Dezimalstelle fehlerfrei dargestellt werden. Zum Beispiel hat 0,1 (ein Zehntel) keine exakte Entsprechung im Binärsystem, was dem gleichen Prinzip entspricht, dass es für ein Drittel keine exakte Darstellung im Dezimalsystem gibt.

87voto

Robert Gamble Punkte 101657
  1. Fließkomma-Arithmetik wird fast immer deutlich schneller sein, weil sie direkt von der Hardware unterstützt wird. Bislang unterstützt fast keine weit verbreitete Hardware die Dezimalarithmetik (obwohl sich dies ändert, siehe Kommentare).
  2. Finanzielle Anträge sollten つねに Verwenden Sie Dezimalzahlen. Die Zahl der Horrorgeschichten, die sich aus der Verwendung von Fließkommazahlen in Finanzanwendungen ergeben, ist endlos, Sie sollten in der Lage sein, viele solcher Beispiele mit einer Google-Suche zu finden.
  3. Die Dezimalarithmetik kann zwar deutlich langsamer sein als die Fließkommaarithmetik, aber wenn Sie nicht viel Zeit mit der Verarbeitung von Dezimaldaten verbringen, sind die Auswirkungen auf Ihr Programm wahrscheinlich vernachlässigbar. Wie immer sollten Sie ein entsprechendes Profiling durchführen, bevor Sie sich über den Unterschied Gedanken machen.

15 Stimmen

+1: Vermeiden Sie eine vorzeitige Optimierung. Messen Sie zuerst, optimieren Sie erst, wenn Sie Beweise haben.

1 Stimmen

"keine weit verbreitete Hardware..." IBM Mainframes (immer noch bemerkenswert populär) haben Hardware-Dezimalstellen.

4 Stimmen

Neue Maschinen, die den neuen IEEE-754-Gleitkommastandard unterstützen, verfügen über Hardware-Unterstützung für Dezimalarithmetik. IBM Power6 ist eine solche Maschine, glaube ich.

27voto

Norman Ramsey Punkte 193087

Hier gibt es zwei voneinander zu trennende Fragen. Zum einen geht es darum, ob das Double über eine ausreichende Genauigkeit verfügt, um alle benötigten Bits zu speichern, und zum anderen darum, wo es Ihre Zahlen genau darstellen kann.

Was die exakte Darstellung angeht, sind Sie zu Recht vorsichtig, denn ein exakter Dezimalbruch wie 1/10 hat kein exaktes binäres Gegenstück. Wenn Sie jedoch wissen, dass Sie nur 5 Dezimalstellen Genauigkeit benötigen, können Sie Folgendes verwenden skaliert Arithmetik, bei der man mit Zahlen arbeitet, die mit 10^5 multipliziert werden. Wenn Sie also zum Beispiel 23,7205 genau darstellen wollen, stellen Sie es als 2372050 dar.

Schauen wir uns an, ob die Genauigkeit ausreicht: Die doppelte Genauigkeit beträgt 53 Bit. Das entspricht einer Genauigkeit von 15+ Dezimalstellen. Dies würde Ihnen also fünf Nachkommastellen und 10 Vorkommastellen ermöglichen, was für Ihre Anwendung ausreichend zu sein scheint.

Ich würde diesen C-Code in eine .h-Datei einfügen:

typedef double scaled_int;

#define SCALE_FACTOR 1.0e5  /* number of digits needed after decimal point */

static inline scaled_int adds(scaled_int x, scaled_int y) { return x + y; }
static inline scaled_int muls(scaled_int x, scaled_int y) { return x * y / SCALE_FACTOR; }

static inline scaled_int scaled_of_int(int x) { return (scaled_int) x * SCALE_FACTOR; }
static inline int intpart_of_scaled(scaled_int x) { return floor(x / SCALE_FACTOR); }
static inline int fraction_of_scaled(scaled_int x) { return x - SCALE_FACTOR * intpart_of_scaled(x); }

void fprint_scaled(FILE *out, scaled_int x) {
  fprintf(out, "%d.%05d", intpart_of_scaled(x), fraction_of_scaled(x));
}

Wahrscheinlich gibt es ein paar schwierige Stellen, aber das sollte für den Anfang reichen.

Kein Overhead für Addition, Kosten einer Multiplikation oder Division verdoppelt.

Wenn Sie Zugang zu C99 haben, können Sie auch die skalierte Ganzzahlarithmetik mit der Funktion int64_t 64-Bit-Integer-Typ. Was schneller ist, hängt von Ihrer Hardware-Plattform ab.

0 Stimmen

Wo liegen hier die Grenzen der Multiplikation und Division?

1 Stimmen

Kaum zu glauben, dass dies nicht höher bewertet wurde, denn es ist eine viel bessere Antwort für den Auftraggeber, der Geschwindigkeitsverbesserungen aufgrund der Beobachtung von Leistungsengpässen suchte. Skalieren und Entkalken ist eine gute Antwort, die Rundungsfehler vollständig vermeidet und gleichzeitig die Geschwindigkeit um den Faktor 100 erhöht.

0 Stimmen

Dies wird als "Festpunktarithmetik" bezeichnet. de.wikipedia.org/wiki/Festkomma_arithmetik Weitere rechnerische Vorteile ergeben sich, wenn Sie Skalierungsfaktoren zur Basis 2 statt zur Basis 10 verwenden, wie Sie es getan haben, und dann mit Bit-Shits dividieren und multiplizieren. Ob dieser Leistungsgewinn wesentlich ist, hängt natürlich von Ihrem Gesamtalgorithmus und der Leistungsprofilerstellung ab

21voto

Craig Punkte 35494

Verwenden Sie für alle finanziellen Berechnungen immer Dezimalzahlen, da Sie sonst ständig auf der Suche nach Rundungsfehlern von 1 Cent sind.

8 Stimmen

+1: Verschwenden Sie keine Zeit auf Leistung, wenn Sie nicht den Beweis haben, dass es sich um das Mathematikpaket handelt.

8 Stimmen

Dies ist keine Antwort auf die Fragen des Fragestellers.

14voto

Jonathan Leffler Punkte 694013
  1. Ja, Software-Arithmetik ist wirklich 100 Mal langsamer als Hardware. Zumindest ist sie sehr viel langsamer, und ein Faktor von 100, plus/minus eine Größenordnung, ist ungefähr richtig. In den schlechten alten Zeiten, als man nicht davon ausgehen konnte, dass jeder 80386 einen 80387-Gleitkomma-Koprozessor hatte, gab es auch eine Software-Simulation der binären Gleitkommaberechnung, und die war langsam.
  2. Nein, Sie leben in einer Fantasiewelt, wenn Sie glauben, dass eine rein binäre Gleitkommazahl jemals alle Dezimalzahlen exakt darstellen kann. Binäre Zahlen können Hälften, Viertel, Achtel usw. kombinieren, aber da eine exakte Dezimalzahl von 0,01 zwei Faktoren von einem Fünftel und einen Faktor von einem Viertel erfordert (1/100 = (1/4)*(1/5)*(1/5)) und da ein Fünftel keine exakte Darstellung im Binärsystem hat, können Sie nicht alle Dezimalwerte mit binären Werten exakt darstellen (denn 0,01 ist ein Gegenbeispiel, das nicht exakt dargestellt werden kann, aber repräsentativ für eine riesige Klasse von Dezimalzahlen ist, die nicht exakt dargestellt werden können).

Sie müssen also entscheiden, ob Sie die Rundung vor dem Aufruf von ToString() vornehmen können oder ob Sie einen anderen Mechanismus finden müssen, der die Rundung Ihrer Ergebnisse bei der Umwandlung in eine Zeichenkette übernimmt. Oder Sie können weiterhin die Dezimalarithmetik verwenden, da sie genau bleibt und schneller wird, sobald Maschinen auf den Markt kommen, die die neue IEEE 754 Dezimalarithmetik in Hardware unterstützen.

Obligatorischer Querverweis: Was jeder Informatiker über Fließkommaarithmetik wissen sollte . Dies ist eine von vielen möglichen URLs.

Informationen zur Dezimalarithmetik und dem neuen Standard IEEE 754:2008 finden Sie hier Speleotrove Standort.

0 Stimmen

1/100 benötigt einen Teiler von 5 sowie einen binären Bruch - oder genauer gesagt, zwei Faktoren von fünf: 1/100 = (1/4)*(1/5)*(1/5). Das 1/4 kann genau in einer binären Fließkommazahl dargestellt werden, die 1/5-Faktoren nicht.

0 Stimmen

@LawrenceDol Natürlich sind sie das, und niemand hat etwas anderes behauptet. Was Jonathan なすった Richtig ist, dass sie nicht exakt binär dargestellt werden können. Hoffentlich hat sich Ihre Verwirrung in den letzten Jahren aufgelöst.

8voto

Verwenden Sie einfach einen Long und multiplizieren Sie mit einer Potenz von 10. Danach dividierst du durch die gleiche 10er-Potenz.

1 Stimmen

Man könnte meinen, dass der Typ Decimal auf diese Weise vom Datenbankserver implementiert werden könnte, um eine wettbewerbsfähige Leistung zu erzielen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X