AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

SetRoundMode wirkt nicht immer

Ein Thema von Jasocul · begonnen am 15. Mär 2024 · letzter Beitrag vom 15. Mär 2024
Antwort Antwort
Benutzerbild von Jasocul
Jasocul

Registriert seit: 22. Sep 2004
Ort: Delmenhorst
1.355 Beiträge
 
Delphi 11 Alexandria
 
#1

SetRoundMode wirkt nicht immer

  Alt 15. Mär 2024, 11:40
Delphi-Version: 11 Alexandria
Kleines Gimmick zum Wochenende.
Da ich gerade ein größeres Refactoring mache, wollte ich alte (etwas merkwürdige) Berechnungen mit Rundungen überarbeiten.
Dabei hatte ich Source, der etwa so aussah:
Delphi-Quellcode:
  Result := Result * Faktor;
  SetRoundMode(rmUp);
  Result := SimpleRoundTo(Result); // Oder war es RoundTo(Result, -2) ?
  SetRoundMode(rmNearest);
Ergebnis: Die Rundung hat nicht funktioniert, wie es vorgesehen war.
Dann wie folgt geändert:
Delphi-Quellcode:
  SetRoundMode(rmUp);
  Result := Result * Faktor;
  Result := SimpleRoundTo(Result);
  SetRoundMode(rmNearest);
Ergebnis: Jetzt funktioniert es.
Es scheint so, als müssten die verwendeten Variablen im gesamten Kontext den RoundMode haben. Ich habe das nicht weiter geprüft und im Endeffekt verwerfen müssen. Der Code ist also auch aus dem Kopf geschrieben.
Ich bin auf die Lösung gekommen, als ich mit dem Debugger nach der Rundung nochmal in die Berechnnungszeile gegangen bin und die Schritte wiederholt habe. Ich dachte erst, dass die Rundungsfunktion doppelt durchlaufen werden müsste (manchmal macht Delphi schon merkürdige Dinge). Aber es war halt die andere Lösung des Problems.

Leider bin ich am WE nicht online. Vielleicht ist das nur bei mir so. Aber vielleicht hat jemand Lust, das selbst mal zu testen. Dann wüsste ich zumindest, ob es an Delphi oder mir liegt.
Peter
  Mit Zitat antworten Zitat
Andreas13

Registriert seit: 14. Okt 2006
Ort: Nürnberg
719 Beiträge
 
Delphi XE5 Professional
 
#2

AW: SetRoundMode wirkt nicht immer

  Alt 15. Mär 2024, 12:04
Hallo Peter,
es kommt bereits in der ersten Zeile des ersten Codes Result := Result * Faktor; bei der Multiplikation zu einer Rundung. Wenn der Rundungsmodus zuvor ein anderer war als der von Dir gewünschte, dann wurde nach diesem gerundet.

PS:
Mit GetRoundMode http://docwiki.embarcadero.com/Libra...h.GetRoundMode kannst Du den alten (= "falschen") Rundungsmodus ermitteln.
Grüße, Andreas
Wenn man seinem Nächsten einen steilen Berg hinaufhilft, kommt man selbst dem Gipfel näher. (John C. Cornelius)

Geändert von Andreas13 (15. Mär 2024 um 12:07 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Jasocul
Jasocul

Registriert seit: 22. Sep 2004
Ort: Delmenhorst
1.355 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: SetRoundMode wirkt nicht immer

  Alt 15. Mär 2024, 12:24
Wieso sollte es in der ersten Zeile zu einer Rundung kommen? Es wird dort keine Rundungsfunktion aufgerufen.
Der Roundmode war übrigens rmNearest.
Peter
  Mit Zitat antworten Zitat
Andreas13

Registriert seit: 14. Okt 2006
Ort: Nürnberg
719 Beiträge
 
Delphi XE5 Professional
 
#4

AW: SetRoundMode wirkt nicht immer

  Alt 15. Mär 2024, 13:53
Tja, das sind die Tücken der Realzahl-Arithmetik (floating point operations)… Hier wird immer gerundet, von ganz-ganz-ganz-ganz seltenen Ausnahmen abgesehen. Das kommt vom Grundproblem der binären Darstellung von reellen Zahlen mit einer endlichen Anzahl von Bits:
Denn selbst zwischen zwei beliebig dicht beieinander liegenden reellen Zahlen existieren immer unendlich viele weitere reelle Zahlen. Noch schlimmer, ihre Menge ist sogar mächtiger (= „mehr als unendlich“): nämlich überabzählbar (s. z. B. https://de.wikipedia.org/wiki/Unendliche_Menge).

Kurz und gut: Für die binäre Codierung von unendlich vielen Zahlen benutzt der Computer lediglich eine endliche Anzahl von Bits:
z.B.
Real: 6 Bytes (= 48 Bits)
Double: 8 Bytes (= 64 Bits)
Extended: 10 Bytes (= 80 Bits) etc.

Selbst Typen von Multipräzisions-Arithmetik (MPA) haben eine endliche Bitlänge: z.B. 120, 360, 1024 Bits etc.

Fazit:
Der Computer kann reelle Zahlen nicht exakt darstellen! Selbst bei der Zuweisung wie z. B.
Zahl:= 0.1; wird gerundet, weil das binäre Äquivalent von 0.1 unendlich viele Bits hat.
Also rundet der Prozessor immer!

Die Anweisung SetRoundMode(..); ist gar nicht für die Benutzer gedacht, denn dies beeinflußt die gesamte Arbeitsweise des Prozessors. Ich würde die Standardeinstellung rmNearest behalten, sonst können gewohnte mathematische Rundungsregeln außer Kraft gesetzt werden und unerwartete Resultate liefern.

Runden solltest Du selber ganz am Ende aller Berechnungen mittels
Math.SimpleRoundTo , System.Round oder System.Math.RoundTo auf die gewünschte Stellenzahl. Wenn manchmal ein unschönes Ergebnis wie 1.999999999999999999 rauskommt, anstelle von 2.000000000000000000, liegt es nicht an Dir.
Da helfen nur mehr, vieeeeeeeeel mehr Stellen, also mindestens Extended anstelle von Double, wenn Du auf Double runden willst. Oft genügt das auch nicht.
Ich benutze MPA-Routinen mit 100 .. 200 Stellen, die ich dann auf Extended (18..19 Stellen) oder Double (14..15 Stellen) runde: Dann ist der kleine Schönheitsfehler (mit groooßem Aufwand) beseitigt...
Grüße, Andreas
Wenn man seinem Nächsten einen steilen Berg hinaufhilft, kommt man selbst dem Gipfel näher. (John C. Cornelius)

Geändert von Andreas13 (15. Mär 2024 um 14:30 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Jasocul
Jasocul

Registriert seit: 22. Sep 2004
Ort: Delmenhorst
1.355 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: SetRoundMode wirkt nicht immer

  Alt 15. Mär 2024, 14:40
Das würde bedeuten, dass Delphi intern auch den Roundmode benutzt, wenn ohne Rundungsfunktion gerechnet wird. Das halte ich für unwahrscheinlich. Ansonsten ist mir das Thema Gleitzahlarythmetik bekannt. Ist zwar lange her, aber ein paar Semester Mathe-Studium habe ich hinter mir.
Peter
  Mit Zitat antworten Zitat
Andreas13

Registriert seit: 14. Okt 2006
Ort: Nürnberg
719 Beiträge
 
Delphi XE5 Professional
 
#6

AW: SetRoundMode wirkt nicht immer

  Alt 15. Mär 2024, 15:31
Sorry Peter,
Das würde bedeuten, dass Delphi intern auch den Roundmode benutzt, wenn ohne Rundungsfunktion gerechnet wird.
aber das verstehe ich nicht ganz.
Könntest Du das bitte etwas verdeutlichen?
Grüße, Andreas
Wenn man seinem Nächsten einen steilen Berg hinaufhilft, kommt man selbst dem Gipfel näher. (John C. Cornelius)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#7

AW: SetRoundMode wirkt nicht immer

  Alt 15. Mär 2024, 15:50
Die Delphi-"Funktionen", arbeiten fast alle mit der FPU und dieser Modus wird in der FPU eingestellt (ich glaub nur für den aktuellen Thread).
Und Jupp, ganzzahlig (Integer) wird in der CPU berechnet, nicht in der FPU, also in den normalen Registern ala EAX anstatt ST0.

Somit hat ein Umstellen, innerhalb des Hauptthreads, auch Einfluß auf alles Mögliche, wie z.B. FireMonkey.
$2B or not $2B
  Mit Zitat antworten Zitat
Benutzerbild von Jasocul
Jasocul

Registriert seit: 22. Sep 2004
Ort: Delmenhorst
1.355 Beiträge
 
Delphi 11 Alexandria
 
#8

AW: SetRoundMode wirkt nicht immer

  Alt 15. Mär 2024, 18:54
Die Delphi-"Funktionen", arbeiten fast alle mit der FPU und dieser Modus wird in der FPU eingestellt (ich glaub nur für den aktuellen Thread).
Und Jupp, ganzzahlig (Integer) wird in der CPU berechnet, nicht in der FPU, also in den normalen Registern ala EAX anstatt ST0.

Somit hat ein Umstellen, innerhalb des Hauptthreads, auch Einfluß auf alles Mögliche, wie z.B. FireMonkey.
Roundmode ist threadsafe. Deine Vermutung trifft da zu.
Die Einstellung des Mode wirkt sich auf die FPU aus, wenn ich die DOH richtig verstanden habe. Allerdings bin ich davon ausgegangen, dass sich das nur auswirkt, wenn Rundungsfunktion aufgerufen werden. Das Verhalten, das ich festgestellt habe, würde bedeuten, dass es sich auf jede Rechenoperation in der FPU auswirkt.

Das wäre für mich jetzt nachvollziehbar, aber da könnte der Hinweis in der Hilfe deutlicher sein, dass von der Einstellung nicht nur die Rundungsfunktionen in Delphi betroffen sind.
Peter
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#9

AW: SetRoundMode wirkt nicht immer

  Alt 15. Mär 2024, 19:42
Floats sind immer etwas gerundet, da sie keine exakten irrationalen Werte speichern können,
vor allem beim Übergang von Extended/Double/Single in Zwischenberechnungen.

Die genaue Funktion wirst du wohl beim Bei Google suchenFLDCW oder Delphi-Referenz durchsuchenSet8087CW finden, bzw. beim Bei Google suchenLDMXCSR/Delphi-Referenz durchsuchenSetMXCSR für SSE (Delphi-Referenz durchsuchenSetSSERoundMode).
Und zusätzlich noch Delphi-Referenz durchsuchenSetPrecisionMode (ebenfalls FLDCW/Set8087CW)




https://docwiki.embarcadero.com/RADS...int_Arithmetic
https://docwiki.embarcadero.com/RADS...elphi_for_x64)
$2B or not $2B

Geändert von himitsu (15. Mär 2024 um 20:01 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:04 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz