Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Rundungsproblem in Delphi (https://www.delphipraxis.net/216612-rundungsproblem-delphi.html)

norwegen60 27. Jan 2025 22:40

Rundungsproblem in Delphi
 
Hallo
ich habe ein ganz seltsamens Rundungsproblem, das ich mit einer bestimmten Zahl festgestellt habe.
Ich verwende folgenden Algorithmus zum Runden
Delphi-Quellcode:
function RoundToDecimals(rValue: Real; iDecimals: Integer): Real;
var
  Faktor,
  Wert: extended;
  iValue:Int64;
begin
  Faktor := IntPower (10, iDecimals);

  Wert:= rValue * Faktor;

  if rValue > 0 then
    Wert := Wert + 0.5
  else
    Wert := Wert - 0.5;

  iValue := trunc(Wert);   // Hier zeigt Überwachte Ausdrücke iValue = 184132 und Wert = 184133 an

  Wert := iValue / Faktor;
  Result  := Wert;
end;
Ich habe auch alle mögliche andere Vorschläge ausprobiert, aber keiner hat sicher geholfen.

Das Problem tritt nicht auf, wenn ich direkt RoundToDecimals(184.1325, 3) aufrufe.#
Sobald ich die Zahl 184.1325 aber aus einem EditFeld per TryStrToFloat('184.1325', rValue) ermittle, habe ich das Problem
Ich habe es auch mit Double und Extended probiert. Ohne Erfolg

Ich weiß keine andere Zahl, wo das auftritt, muss aber sicherstellen, dass immer korrekt gerundet wird.

Kann mir jemand sagen, woran das liegen könnte und wie ich es beheben kann.

Ich verwende Delphi 7

Vielen Dank
Gerd

Jasocul 28. Jan 2025 05:16

AW: Rundungsproblem in Delphi
 
Entgegen der allgemeinen Vorstellung, dass Trunc wirklich einfach abschneidet, rundet Trunc gegen "0". Bei unglücklicher Konstellation kann es dann zu solchen Phänomenen kommen.
Zum besseren Verständnis siehe diesen Beitrag

haentschman 28. Jan 2025 05:51

AW: Rundungsproblem in Delphi
 
Hallöle...8-)
Zitat:

Ich verwende folgenden Algorithmus zum Runden
Was spricht gegen RoundTo oder SimpleRoundTo? :gruebel:

Zitat aus docwiki:
"SimpleRoundTo ist das herkömmliche "Abrunden", das in der Schule gelernt wurde."

Erklärung:
https://mathiaspannier.wordpress.com...simpleroundto/
https://docwiki.embarcadero.com/RADS...eitkommawerten

Zitat:

dass immer korrekt gerundet wird
...was ist für dich korrekt? (siehe Beispiele im docwiki)

norwegen60 28. Jan 2025 08:02

AW: Rundungsproblem in Delphi
 
Hallo zusammen,

danke fürs Feedback.

Zunächst muss ich sagen, dass mein Problem gelöst zu sein scheint. Ich habe auch in der Funktion die beiden Deklarationen
Delphi-Quellcode:
Faktor, Wert: extended;
auf Real gesetzt. Hatte den Code hier aus DP kopiert und nur die Eingangsparameter an meine Werte angepasst.
Trotzdem bin ich verwundert, dass sowohl in "Überwachte Ausdrücke" als auch wenn ich mit der Maus auf Wert fahre, genau 184133 angezeigt wird. Also keinerlei Kommastellen. Trotzdem kommt im Integer nach Trunc 184132 an

Zitat:

Zitat von Jasocul (Beitrag 1545682)
Entgegen der allgemeinen Vorstellung, dass Trunc wirklich einfach abschneidet, rundet Trunc gegen "0".

Wie oben beschrieben, gibt es bei der Zahl, zumindest wenn ich auf die Anzeige schaue, nichts mehr zu runden. Ich hatte ja zuvor 184132.5 + 0.5 gerechnet. Der Trunc soll also nur noch die Zahl in eine Integer verwandeln. Tut er auch, aber eben 184132, also 1 zu niedrig

Zitat:

Zitat von haentschman (Beitrag 1545684)
Was spricht gegen RoundTo oder SimpleRoundTo?

Dass dort die übergebene 185.1325 auch auf 184.132 abgerundet wird. Wie gesagt tritt das Problem nur auf, wenn ich die Zahl zuvor per TryStrToFloat aus einem String umgewandelt habe.

Und in meinem Code multipliziere ich die Zahl ja sogar noch, sogar die gewünschten Stellen komplett ganze Zahlen sind und addiere dann noch 0.5 dazu. Trotzdem wird zum Schluss wieder abgerundet. Damit ist die Ganz-Zahl ja schon genau das, was ich will.

Gewünscht ist eine kaufmännische Rundung. D.h. 184.1325 ergibt bei 3 Stellen 184.133 und 184.13249999 ergibt 184.132

Jasocul 28. Jan 2025 09:14

AW: Rundungsproblem in Delphi
 
Zitat:

Zitat von norwegen60 (Beitrag 1545690)
Zitat:

Zitat von Jasocul (Beitrag 1545682)
Entgegen der allgemeinen Vorstellung, dass Trunc wirklich einfach abschneidet, rundet Trunc gegen "0".

Wie oben beschrieben, gibt es bei der Zahl, zumindest wenn ich auf die Anzeige schaue, nichts mehr zu runden. Ich hatte ja zuvor 184132.5 + 0.5 gerechnet. Der Trunc soll also nur noch die Zahl in eine Integer verwandeln. Tut er auch, aber eben 184132, also 1 zu niedrig

Die Anzeige lügt. Dort werden gerundete Werte angezeigt.
Hast du dir den Link angesehen? Dort wird nahezu das gleiche Problem dargestellt und Lösungen sind dort auch.

norwegen60 28. Jan 2025 13:55

AW: Rundungsproblem in Delphi
 
Zitat:

Zitat von Jasocul (Beitrag 1545696)
Die Anzeige lügt. Dort werden gerundete Werte angezeigt.

:-D

Aber jetzt habe ich hoffentlich wieder in Normalmodus geschalten. Hätte schon vorher passieren müssen. TRUNC schneidet ja nur ab => 184132.999999999999999999999999 wird zu 184132 :oops:

Werden jetzt in die Formel noch Round(184132.9999) setzen und bin dann hoffentlich auf der sicheren Seite.

Jasocul 28. Jan 2025 14:28

AW: Rundungsproblem in Delphi
 
Leider bist du damit auch nicht auf der sicheren Seite.

Round verwendet das sogenannte Bankers Rounding. Dabei wird 3,5 zu 4 gerunden. Allerdings 4,5 auch zu 4.
Das Prinzip dahinter: Bei x,5 wird zur nächsten geraden Zahl gerundet.

Du brauchst SimpleRoundTo. Das müsste in der Unit System.Math stehen. Könnte bei D7 aber auch anders sein :wink:
SimpleRoundTo macht es so, wie es bei uns in der Schule gelehrt wird. Bei x.5 wird aufgerundet. Und das passt dann auch zu deiner Anforderung.

haentschman 28. Jan 2025 14:41

AW: Rundungsproblem in Delphi
 
D7 :gruebel:

ChatGPT sagt SimpleRoundTo für D7:
Zitat:

Ja, Delphi 7 enthält bereits die Unit Math, jedoch die Funktion SimpleRoundTo wurde erst später in Delphi 2006 (Borland Developer Studio 2006) eingeführt.
ungetestet:
Delphi-Quellcode:
// Beispielimplementierung von SimpleRoundTo in Delphi 7
function SimpleRoundTo(const AValue: Extended; const ADigit: Integer): Extended;
var
  Factor: Extended;
begin
  Factor := IntPower(10, ADigit);
  Result := Round(AValue * Factor) / Factor;
end;
Original D12:
Delphi-Quellcode:
function SimpleRoundTo(const AValue: Extended; const ADigit: TRoundToRange = -2): Extended;
var
  LFactor: Extended;
begin
  LFactor := IntPower(10.0, ADigit);
  if AValue < 0 then
    Result := Int((AValue / LFactor) - 0.5) * LFactor
  else
    Result := Int((AValue / LFactor) + 0.5) * LFactor;
end;

norwegen60 28. Jan 2025 16:44

AW: Rundungsproblem in Delphi
 
Zitat:

Zitat von haentschman (Beitrag 1545712)
Delphi-Quellcode:
function SimpleRoundTo(const AValue: Extended; const ADigit: TRoundToRange = -2): Extended;
var
  LFactor: Extended;
begin
  LFactor := IntPower(10.0, ADigit);
  if AValue < 0 then
    Result := Int((AValue / LFactor) - 0.5) * LFactor
  else
    Result := Int((AValue / LFactor) + 0.5) * LFactor;
end;

Das war, was ich ursprünglich verwendet habe, was aber beim Aufruf die 182.1325 in 182.132 gerundet hat.
Ich hatte die Funktion als Overload mit Extended und Double. Habe mittlerweile raus gefunden, dass Aufruf mit Extended korrekt auf 182.133 rundet, Aufruf mit Double aber auf 182.132. Das Programm in dem ich arbeite, arbeitet aber nahezu durchgängig mit Double und das umzustellen wäre ein ziemlicher Akt. Und wer weiß was da dann dafür passiert.

Und natürlich ist die Umstellung von TRUNC auf ROUND auch nicht zielführend. Kann ja nicht zuerst 0.5 dazuaddieren und dann noch runden.

Ich lasse es bei meinem "Bastelansatz" und der sieht bei Double jetzt so aus
Delphi-Quellcode:
function RoundToDecimals(rValue: Double; iDecimals: Integer): Double;
var
  rFactor: Double;

begin
  rValue := rValue + 0.0000000000001;
  rFactor := IntPower(10.0, -iDecimals);
  if rValue < 0 then
    Result := Int((rValue / rFactor) - 0.5) * rFactor
  else
    Result := Int((rValue / rFactor) + 0.5) * rFactor;
  exit;
end;
Verwende das Ganze nur zum Runden von Dezimalen, und iDecimals wird immer nur positiv übergeben
Die 0.0000000000001 war der unterste Wert, der meine Wert so korrigiert hat, dass danach 184.133 raus kam. Egal wo ich die Funktion verwende, mehr als 10 Dezimalen werden nie angezeigt (Normal sind max 6). Die Erhöhung ist also nicht relevant

Ach ja: Die D7 Formel kommt auch auf 184.132

TurboMagic 28. Jan 2025 20:06

AW: Rundungsproblem in Delphi
 
Es ist auch aus einem anderen Grund nicht ratsam auf Extended zu wechseln:
auf allen Plattformen außer Win32 wird Extended auf Double gemappt.
Evtl. ist MacOS noch eine Ausnahme.

Grund: Extended ist ein FPU interner Datentyp der x87 FPU.
Die gibt's ja aber nicht überall und bei x64 wollte man lieber auf SSE Unterstützung
(zumindest rudimentär) gehen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:36 Uhr.
Seite 1 von 2  1 2      

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-2025 by Thomas Breitkreuz