Frage

AFAIK, Währungstyp in Delphi Win32 hängt von der Schwimmpunktpräzision des Prozessors ab. Aus diesem Grund habe ich Rundungsprobleme, wenn ich zwei Währungswerte vergleicht und je nach Maschine unterschiedliche Ergebnisse zurückgibt.

Im Moment verwende ich die gleiche Mehrwertfunktion, die einen Epsilon -Parameter = 0,009 überholt, da ich nur eine Präzision von 2 Dezimalstellen benötige.

Gibt es einen besseren Weg, um dieses Problem zu vermeiden?

War es hilfreich?

Lösung

Der Währungstyp in Delphi ist eine 64-Bit-Ganzzahl, die von 1/10.000 skaliert ist. Mit anderen Worten, sein kleinste Inkrement entspricht 0,0001. Es ist nicht anfällig für Präzisionsprobleme, wie es schwimmender Punktcode ist.

Wenn Sie jedoch Ihre Währungszahlen mit Floating-Punkt-Typen multiplizieren oder Ihre Währungswerte dividieren, muss die Rundung auf die eine oder andere Weise ausgearbeitet werden. Die FPU steuert diesen Mechanismus (er heißt das "Steuerwort"). Die Mathematikeinheit enthält einige Verfahren, die diesen Mechanismus steuern: insbesondere SetRoundMode. Sie können die Effekte in diesem Programm sehen:

{$APPTYPE CONSOLE}

uses Math;

var
  x: Currency;
  y: Currency;
begin
  SetRoundMode(rmTruncate);
  x := 1;
  x := x / 6;
  SetRoundMode(rmNearest);
  y := 1;
  y := y / 6;
  Writeln(x = y); // false
  Writeln(x - y); // 0.0001; i.e. 0.1666 vs 0.1667
end.

Es ist möglich, dass eine von Ihnen verwendete Bibliothek von Drittanbietern das Steuerwort auf einen anderen Wert einstellt. Möglicherweise möchten Sie das Kontrollwort (dh der Rundungsmodus) am Ausgangspunkt Ihrer wichtigen Berechnungen ausdrücklich festlegen.

Wenn Ihre Berechnungen jemals in einen einfachen schwimmenden Punkt und dann wieder in die Währung übertragen werden, sind alle Wetten zu schwer zu prüfen. Stellen Sie sicher, dass alle Ihre Berechnungen in der Währung sind.

Andere Tipps

Nein, Währung ist kein schwimmender Punkttyp. Es handelt sich um eine Dezimalzahl für festverwaltere, die mit Integer-Speicher implementiert wird. Es kann genau verglichen werden und hat nicht die Rundungsprobleme von beispielsweise doppelt. Wenn Sie daher in Ihren Währungsvariablen ungenickere Werte sehen, ist das Problem nicht der Währungsart selbst, sondern das, was Sie in ihn einfügen. Höchstwahrscheinlich haben Sie eine Floating-Punkt-Berechnung woanders in Ihrem Code. Da Sie diesen Code nicht zeigen, ist es schwierig, in dieser Frage mehr zu helfen. Die Lösung besteht jedoch im Allgemeinen darin, Ihre schwimmenden Punktzahlen zur korrekten Genauigkeit zu runden, bevor sie in der Währungsvariable gespeichert werden, anstatt einen ungenauen Vergleich mit den Währungsvariablen durchzuführen.

Schneller und sicherer Art, zwei zu vergleichen currency Werte müssen die Variablen sicherlich ihrer internen abbilden Int64 Darstellung:

function CompCurrency(var A,B: currency): Int64;
var A64: Int64 absolute A;
    B64: Int64 absolute B;
begin
  result := A64-B64;
end;

Dies vermeidet jeden Rundungsfehler während des Vergleichs (Arbeit mit *10000 Ganzzahlwerten) und ist schneller als die Standard-FPU-basierte Implementierung (insbesondere unter 64-Bit-XE2-Compiler).

Sehen Dieser Artikel für weitere Informationen.

Wenn Ihre Situation wie meine ist, finden Sie diesen Ansatz möglicherweise hilfreich. Ich arbeite hauptsächlich in der Gehaltsabrechnung. Wenn ein Unternehmen 3 Abteilungen gesagt hat und die Kosten eines Mitarbeiters gleichmäßig unter diesen drei Abteilungen verlangen möchte, gibt es viele Male, in denen es zu Rundproblemen kommt.

Was ich getan habe, ist, die Abteilungen durch die Gesamtkosten zu verlangen und die Kosten für eine subtotale (Währung) Variable zu erheben. Wenn die Schleifenvariable jedoch der Grenze entspricht, anstatt sich mit der Fraktion zu multiplizieren, subtrahiere ich die subtotale Variable von den Gesamtkosten und setze diese in der letzten Abteilung ein. Da die Journaleinträge, die aus diesem Prozess resultieren, immer ausgleichen müssen, glaube ich, dass es immer funktioniert hat.

Siehe Thread:

D7 / Dunit: Alle Scheckkequals (Währung, Währung) Tests scheitern plötzlich ...

https://forums.codegear.com/thread.jspa?threadid=16288

Es sieht so aus, als ob sich unsere Entwicklungsworkstationen verändert haben. Wir haben die Hauptursache nicht gefunden, sondern auf zwei Computern, die Windows 2000 SP4 und unabhängig von der Version von GDS32.DLL (Interbase 7.5.1 oder 2007) und Delphi (7 und 2009), diese Zeile, ausführen

TIBDataBase.Create(nil);

ändert den Wert von 8087 Kontrollwort von 1372 USD auf 1272 USD jetzt.

Und alle Währungsvergleiche in Unit -Tests werden mit lustigen Nachrichten wie fehlschlagen

Expected: <12.34> - Found: <12.34>

Das GDS32.DLL wurde nicht geändert, daher denke ich, dass es in dieser Bibliothek eine Abhängigkeit von einer DLL von Dritten gibt, die das Kontrollwort ändert.

Um mögliche Probleme mit Währungsrundung in Delphi zu vermeiden, verwenden Sie 4 Dezimalstellen.

Dadurch wird sichergestellt, dass Sie bei der Berechnung mit sehr geringen Mengen nie Rundungsprobleme haben.

"Been there. Done That. Written the unit tests."

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top