![]() |
Rundungs-Probleme bei Ausgabe als String
Moin,
ich habe seit langen mal wieder ein Problem mit der Rundung. Die grundsätzlichen Probleme mit der Darstellung von Fließkomma-Werten in binärer Form sind mir klar. Auch die interne Datenhaltung verstehe isch (einigermaßen :roll:). Wenn mir alles klar wäre, würde ich jetzt vermutlich aber nicht fragen: Ich habe einen Wert aus einer Datenbank, der in eine Delphi-Double-Variable kopiert wird. Dieser Wert soll dann in einem String ausgegeben werden. Dabei sollen fix 5 Nachkommastellen in der String-Ausgabe genutzt werden.
Delphi-Quellcode:
function Test: string;
var lWert: Double; lAnzahlNachkommaStellen: integer; begin Result := ''; lAnzahlNachkommaStellen := 5; lWert := 13.727735; Result := Format( '%.*f', [ lAnzahlNachkommaStellen, lWert]); end; Leider ist es bei mir so, das dabei bei unterschiedlichen Aufrufen unterschiedliche Werte zurückgeliefert werden. Beim ersten Aufruf wird bei mir aktuell reproduzierbar "13.72773" ausgegeben, bei den folgenden "13.72774". Dazwischen habe ich mit dem Programm einiges gemacht. Ich habe auch schon einige der anderen Umwandlungen genutzt: FloatToStr( ...), FloatToStrF( ...); diese verhalten sich anscheinend identisch. Ich denke, in meinem Fall ist es nicht das "Bankers Rounding", dass mir die Werte verstellt. Da müsste dann ja immer der gleiche Wert herauskommen. Auch sage ich nicht, welcher der beiden Werte der richtige sein muss (das hängt ja von der Rundung ab...). Ich benötige nur reproduzierbar gleiche Werte. Ich habe dann zum testen folgende Funktion probiert: ![]()
Delphi-Quellcode:
Diese verhindert aber das Unterschiedliche Ergebnis bei mehreren Aufrufen nicht.
Result := Format( '%.*f',
[ RoundX( lWert, lAnzahlNachkommaStellen)]); Erst wenn ich die Rundungs-Funktion mit "lAnzahlNachkommaStellen + 1" aufrufe, scheint es so, dass ich ein reproduzierbares Ergebnis bekomme... Ist das die korrekte Lösung? Oder stehe ich ganz woanders auf dem Schlauch... Zusätzlich: Entspricht die Funktion RoundX aus der Library nicht auch der Funktion "SimpleRoundTo" aus Delphi (bei mir Berlin 10.1 Up2), wenn für "RoundUp" die "Alternative Implementation" genutzt wird? Kann mir jemand den Unterschied erklären? Was ist hier der Unterschied zwischen "Int" und "Trunc", hat die Reihenfolge von "/" und "*" Einfluss auf das Ergebnis? Vielen Dank |
AW: Rundungs-Probleme bei Ausgabe als String
Zitat:
|
AW: Rundungs-Probleme bei Ausgabe als String
Zitat:
Das ist eine neue Variable. zwischendurch alles freigegeben. Längerer Ablauf. die Variable ist Teil eines TDictionary< Integer, Double>. Der Weg zu der Variable ist aber komplett identisch... Es wird nach der Zuweisung nur Lesend darauf zugegriffen. Es jeweils von mir der selbe Programm-Ablauf angesprochen (Test-Button auf dem Haupt-Formular), wodurch die Daten gelesen werden. Das MouseOver in Delphi über die Variable zeigt immer "13,727735". ein "Frac( lWert)" in der Rundungs-Funktion zeigt bei der Fehlersuche -> Überwachte Ausdrücke beim ersten Durchlauf "0,499999999913257", bei den folgenden "0,5". Somit ist für Delphi die Zahl < ,5 -> also beim ersten Durchlauf immer abrunden. Ich hab schon bei mir nach SetRoundMode u. ä. gesucht aber nichts gefunden. nachdem ich den String erstellt habe,m wird auch eine .dll genutzt, die den erstellten String verarbeitet. Selbst meine kleine Test-Funktion aus meinem letzten Post verhält auch so. zuerst "13,72773", nach dem Aufruf meiner Funktion ""13,72774". Vielleicht ist das der Hinweis, dass da doch was verstellt wird?!? |
AW: Rundungs-Probleme bei Ausgabe als String
Zitat:
Einfach aus Erfahrung: Nach dem Aufruf einer Fremdle kannst Du nie davon ausgehen das die FPU noch den gewünschten State hat. Deshalb for dem Aufruf mit GetFPURoundMode sichern und nachher mit SetFPURoundMode() wieder setzen. |
AW: Rundungs-Probleme bei Ausgabe als String
Schlage vor du machst mal ein ganz kleines Testprogramm dafür.
|
AW: Rundungs-Probleme bei Ausgabe als String
Zitat:
Ich nutze lpsolve für eine lineare Optimierung ( ![]() Das ist eine in C geschriebene .dll. In der Delphi-Zugriffs-Datei steht:
Code:
und in der IDE (Main.pas -> in Delphi geschrieben):
* Important information
* Solver library is compiled for a different Control Word, you should change * the Delphi control Word to avoid foating point operation errors. * _control87 Set8087CW * Visual Studio $9001F -> 639 * GCC $8001F -> 895
Delphi-Quellcode:
Ich habe jetzt meine Kommunikation mit der .dll in folgenden Block gepackt:
initialization
set8087cw(895); end.
Delphi-Quellcode:
Das liefert mir:
//lRoundingMode := GetRoundMode;
l8087CW := Get8087CW; try { Hier der gesammte Zugriff auf die .dll } finally ShowMessage( (* 'alt: ' + TRttiEnumerationType.GetName( lRoundingMode) + sLineBreak + 'neu: ' + TRttiEnumerationType.GetName( GetRoundMode) + sLineBreak + *) 'alt: ' + IntToStr( l8087CW) + sLineBreak + 'neu: ' + IntToStr( Get8087CW) ); //SetRoundMode( lRoundingMode); Set8087CW( l8087CW); end; 1.Aufruf alt: 4991 neu: 610 folgende Aufrufe: alt: 4991 neu: 4962 Da ich den Wert mittels "Set8087CW" nach der .dll zurückstelle, passt "meine" Rundung/String-Erstellung, ohne das ich irgendwie zusätzlich runden muss...(hat auch tatsächlich nicht korrekt funktioniert)! Was mich jetzt aber noch stört: Die .dll hat beim 2ten Aufruf einen anderen Wert gesetzt. Verändert sich dadurch das Verhalten der .dll? (vermutlich, oder?) Könnt ihr mir hier weiterhelfen, oder muss ich das mit dem Erstellern der .dll klären? Darf ich im laufenden Programm Set8087CW nutzen? Was wird dadurch gemacht? Danke, |
AW: Rundungs-Probleme bei Ausgabe als String
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Anhang 48562 Nähere Infos dazu entnimmst du am besten der "Intel Architecture Software Developer's Manual; Vol I: Basic Architecture". Was RC angeht, sind hier die relevanten Werte:
Code:
Round to nearest even = $00B
Round down toward infinity = $01B Round up toward infinity = $10B Round toward zero (trunc) = $11B Zitat:
|
AW: Rundungs-Probleme bei Ausgabe als String
Hallo,
Zitat:
|
AW: Rundungs-Probleme bei Ausgabe als String
Zitat:
|
AW: Rundungs-Probleme bei Ausgabe als String
Zitat:
Zitat:
Code:
Fazit:
keine Berücksichtigung
Dec Hex C RC Exception von mir 4991 137F 0001 0011 0111 1111 Delphi vor erster Start 610 262 10 0110 0010 nach 1. Aufruf 610 262 10 0110 0010 folgende Aufrufe I PC Schutz mit try ... Dec Hex C RC Exception finally 4991 137F 0001 0011 0111 1111 Delphi vor erster Start 610 262 10 0110 0010 nach 1. Aufruf 4962 1362 0001 0011 0110 0010 folgende Aufrufe I PC Schutz mit try... Dec Hex C RC Exception finally + Setzen "37F" 4991 137F 0001 0011 0111 1111 Delphi vor erster Start 895 37F 11 0111 1111 manuell gesetzt 610 262 10 0110 0010 nach 1. Aufruf 895 37F 11 0111 1111 manuell gesetzt 866 362 11 0110 0010 folgende Aufrufe I PC Schutz mit try... Dec Hex C RC Exception finally + Setzen "262" 4991 137F 0001 0011 0111 1111 Delphi vor erster Start 610 262 10 0110 0010 manuell gesetzt 610 262 10 0110 0010 nach 1. Aufruf 610 262 10 0110 0010 manuell gesetzt 610 262 10 0110 0010 folgende Aufrufe
Zitat:
Daher ist das "try ... finally" zum "Merken und Zurücksetzen" schon notwendig, denke ich. Vielen Dank für eure Hilfe! |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:55 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz