Thema: Delphi Single runden

Einzelnen Beitrag anzeigen

Scurra

Registriert seit: 19. Jan 2015
81 Beiträge
 
Delphi 10.3 Rio
 
#1

Single runden

  Alt 18. Mär 2015, 15:50
Hallo zusammen,

ich stehe gerade vor einem Problem: Ich möchte Gleitkommazahlen (vom Typ Single, um genauer zu sein), exakt (d. h. kaufmännisch) runden. Das Runden soll für verschiedene Genauigkeiten (Nachkommastellen) möglich sein. Dazu habe ich mir folgende Funktion geschrieben (ich arbeite hier mit dem Datentyp Extended, weil ich bei der Verwendung von Single noch mehr Probleme habe, obwohl ich in meinem Programm immer Werte vom Typ Single an die Funktion übergebe):

Delphi-Quellcode:
function SetPrecision(aValue: Extended; aPrecision: Integer): Extended;
var
  multi : Single;
begin
  if aPrecision < 0 then Result := aValue
  else
  begin
    multi := IntPower(10, aPrecision);
    if aValue >= 0 then aValue := Trunc(aValue*multi + 0.5)
    else aValue := Trunc(aValue*multi - 0.5);
    Result := aValue/multi;
  end;
end;
Das funktioniert auch fast immer. Allerdings gibt es noch Probleme, wie mir mit dUnit gerade aufgefallen ist. Wenn an der entscheidenen Stelle fürs Runden eine 5 steht, dann kommt es vor, dass falsch gerundet wird, da die Zahlen intern wohl anders gespeichert werden (die Zahl 2.5 ist nicht nur 2.5, sondern so etwas wie 2.4999845). Dann wird natürlich 2.5 nicht auf 3, sondern auf 2 gerundet.

Um dieses Problem zu lösen, hatte ich die Idee, die Funktion SetPrecision zunächst noch einmal rekursiv aufzurufen, etwa mit:
Delphi-Quellcode:
function SetPrecision(aValue: Extended; aPrecision: Integer; firstTime: Boolean = True): Extended;
var
  multi : Single;
begin
  if firstTime then aValue := SetPrecision(aValue, aPrecision+1, false);

  if aPrecision < 0 then Result := aValue
  else
  begin
    multi := IntPower(10, aPrecision);
    if aValue >= 0 then //aValue := Trunc(aValue*multi + 0.5)
    begin
      aValue := aValue*multi;
      aValue := aValue +0.5;
      if Frac(aValue) <> 0 then aValue := Trunc(aValue);
    end
    else aValue := Trunc(aValue*multi - 0.5);
    Result := aValue/multi;
  end;
end;
Das funktioniert leider immer noch nicht. Ich habe den Debugger verwendet, um nachvollziehen zu können, was schief läuft (daher z. B. auch die Zeile if Frac(aValue) <> 0 then aValue := Trunc(aValue); Genau in dieser Zeile tritt das Problem auf. Nehmen wir als Beispiel den Aufruf Aufruf von SetPrecision(x, 4) mit der Variable x = 44.99995 als Single. Laut Debugger ist im zweiten Durchlauf direkt vor Ausführen der oben genannten Zeile aValue = 450000. Intern wird da aber wohl wieder irgendein Rest mitgezogen, so dass die if-Abfrage ein true liefert und nach Trunc(aValue) habe ich 449999. Am Ende steht also der Wert 44.9999 dran, obwohl es 45 sein sollte.

Hat jemand eine Idee, wie ich das Problem in den Griff bekommen kann?

P.S.: Ja, ich habe schon im Internet nach Lösungen gesucht. Wie ich gesehen habe, gibt es schon einige Beiträge dazu, aber mein Problem konnte ich leider nicht lösen.
  Mit Zitat antworten Zitat