Autor Beitrag
Hidden
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2242
Erhaltene Danke: 55

Win10
VS Code, Delphi 2010 Prof.
BeitragVerfasst: Fr 12.07.13 09:18 
Hallo,

In einem meiner Projekte habe ich folgende Zuweisung:
ausblenden Delphi-Quelltext
1:
x := Trunc(  (locX + shift) * FieldW * BufW / ( w * FullW * scale )  );					

scale ist vom Typ Extended, ansonsten sind alles Integers.
Um das unten beschriebene Problem zu umgehen, könnte ich die hintere Klammer auflösen und die entstehenden Divisionen ggf. etwas nach vorne ziehen (vielleicht auch die vordere Klammer (locX + shift) ausmultiplizieren). Wie gehe ich dabei vor, damit der Wert für x möglichst Stabil ist, d.h. damit er für möglichst viele Variablenbelegungen möglichst nahe am richtigen Ergebnis liegt?

Ich nehme an, dass für Überlauf-Stabilität die Zwischenergebnisse möglichst nahe an 1 liegen sollten (Exponent), ich für Genauigkeit aber möglichst viele der Zwischenergebnisse in Integer-Typen halten will (Mantisse), korrekt?

ausblenden Zwischenergebnisse nahe an 1 halten:
1:
x := Trunc(  (locX + shift) * ( FieldW / FullW ) * ( BufW / w ) / scale )  );					

ausblenden Lange in Integer bleiben:
1:
x := Trunc(   ( (locX + shift) * FieldW * BufW ) / (  ( w * FullW ) * scale  )   );					


Auch wird hier spekuliert, dass die Zwischenergebnisse eine reduzierte Genauigkeit haben (statt einer erhöhten :shock:)

Zum Effekt:
Obwohl alle Variablen positiv sind, ist das Ergebnis negativ

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
( x = -144 für

locX = 507
shift = 4578
FieldW = 300
BufW = 1408
w = 1408
FullW = 1500
scale = 7,05219818923083 )


Wenn ich diese Werte im Quelltext für die Variablen einsetze (x := Trunc(  (507 + 4578) * 300 * 1408 / ( 1408 * 1500 * 7.05219818923083 )  );, erhalte ich vom Compiler:
ausblenden Quelltext
1:
[DCC Error] frmMain.pas(494): E2099 Overflow in conversion or arithmetic operation					


lg,
Daniel

_________________
Centaur spears can block many spells, but no one tries to block if they see that the spell is a certain shade of green. For this purpose it is useful to know some green stunning hexes. (HPMoR)


Zuletzt bearbeitet von Hidden am Fr 12.07.13 09:19, insgesamt 1-mal bearbeitet
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Fr 12.07.13 10:02 
user profile iconHidden hat folgendes geschrieben Zum zitierten Posting springen:
9818923083 )[/code]
Wenn ich diese Werte im Quelltext für die Variablen einsetze (x := Trunc(  (507 + 4578) * 300 * 1408 / ( 1408 * 1500 * 7.05219818923083 )  );, erhalte ich vom Compiler:
ausblenden Quelltext
1:
[DCC Error] frmMain.pas(494): E2099 Overflow in conversion or arithmetic operation					
Dein Compiler hat ja auch völlig recht, denn (507 + 4578) * 300 * 1408 = 2147904000 > MaxLongint. Dieser Ausdruck wird ja in Integer-Arithmetik berechnet und führt zum Überlauf. Bei diesen mikrigen Zahlen (und ohne Subtraktionen) braucht man sich nicht um Termumformungen und Statibilität zusorgen, wenn man die richtigen Datenrepresentationen verwendet: Entweder eine Deiner Konstanten als Float schreiben, zB 1408.0, oder eine temporäre extended Variable verwenden.
ausblenden Delphi-Quelltext
1:
2:
tmp := locX + shift;
x := trunc( tmp * FieldW * BufW / ( w * FullW * scale ));
wobei eventuell je nach Datentypen und Wertebereich auch w * FullW * scale überlaufen kann, am sichersten wäre dann etwas wie
ausblenden Delphi-Quelltext
1:
2:
3:
tmp := locX + shift;
tmp := tmp * FieldW * BufW / w / FullW / scale;
x := trunc(tmp);


Zuletzt bearbeitet von Gammatester am Fr 12.07.13 10:13, insgesamt 1-mal bearbeitet
Hidden Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2242
Erhaltene Danke: 55

Win10
VS Code, Delphi 2010 Prof.
BeitragVerfasst: Fr 12.07.13 10:15 
Der volle Divisor (also w * FullW * scale) sollte nicht überlaufen, besonders da scale bereits extended ist.

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Edit: Mist :( - Statt edit auf Antwort geklickt, kann mit vorherigem Beitrag verschmolzen werden.
:welcome: habe ebenfalls 1 Minute nach Thread-Erstellung den Komplett-Quote zwecks Löschen gemeldet, den ich statt eines Edits gemacht hatte :lol:

Edit: Das hier ist, wie man es wahrscheinlich in den meißten Fällen machen sollte (iirc)
user profile iconHidden hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Zwischenergebnisse nahe an 1 halten:
1:
x := Trunc(  (locX + shift) * FieldW / FullW * BufW / w / scale )  );					

_________________
Centaur spears can block many spells, but no one tries to block if they see that the spell is a certain shade of green. For this purpose it is useful to know some green stunning hexes. (HPMoR)


Zuletzt bearbeitet von Hidden am Fr 12.07.13 13:59, insgesamt 1-mal bearbeitet
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Fr 12.07.13 11:38 
Langweilige Info zuerst: wenn man alles in Extended rechnet, funktionierts.

In Delphi bestimmt immer der erste Ausdruck einer Kette, in welchen Typ der Compiler das zusammenfasst. Hier also die 507. Was hilft:
ausblenden Delphi-Quelltext
1:
x := Trunc(  (507.0 + 4578) * 300 * 1408 / ( 1408 * 1500 * 7.05219818923083 )  );					


Mit Variablen wäre z.b. das eine Lösung:
ausblenden Delphi-Quelltext
1:
  x:= Trunc((1.0 * locX + FXShift) * FieldWidth * BufW / (w * FullW * FZoomFact));					

Die 1.0 erzwingt dabei Fließkommaarithmetik, alles ab da passiert auf der FPU.

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."

Für diesen Beitrag haben gedankt: Hidden
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 12.07.13 12:52 
Oder einfach so:
ausblenden Delphi-Quelltext
1:
x := Trunc(  Int64(507 + 4578) * 300 * 1408 / ( 1408 * 1500 * 7.05219818923083 )  );					
Es sollte auch reichen Int64 statt Integer für die Variablen zu nutzen.
Vorausgesetzt der Maximalwert von Int64 wird nicht überschritten, dann geht das natürlich so auch nicht.

Für diesen Beitrag haben gedankt: Hidden, Martok
GuaAck
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 378
Erhaltene Danke: 32

Windows 8.1
Delphi 10.4 Comm. Edition
BeitragVerfasst: Fr 12.07.13 19:19 
Dda gibt es noch einen anderen Aspekt:

Die Reihenfolge der Berechnung und auch der "Zeitpunkt" der Typenumwandlung können von der Compilerversion und vielleicht sogar von Optimierungseinstellungen abhängen.

Wenn es also wirklich auf reproduzierbare Ergebnisse ankommt, dann sollte man alles selbst steuern, z. B. wie vorgeschlagen über Zwischenvariable. Diese Zwischenvariable würde ich sogar z. B. im PUBLIC-Teil der Klasse definieren, so dass der Compiler sie auf jeden Fall anlegen und berechnen muss. Evtl. kann man das auch mit einem Compiler-Schalter für den Code-Bereich erzwingen, dass weiß ich aber nicht.

Gruß
GuaAck
Hidden Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2242
Erhaltene Danke: 55

Win10
VS Code, Delphi 2010 Prof.
BeitragVerfasst: Fr 12.07.13 20:01 
user profile iconGuaAck hat folgendes geschrieben Zum zitierten Posting springen:
Diese Zwischenvariable würde ich sogar z. B. im PUBLIC-Teil der Klasse definieren, so dass der Compiler sie auf jeden Fall anlegen und berechnen muss. Evtl. kann man das auch mit einem Compiler-Schalter für den Code-Bereich erzwingen, dass weiß ich aber nicht.
Hmm, da wäre mir lieber dass andere sich beim Lesen des Programms und Verstehen der public-Variablen nicht wundern müssen.

Wenn es so kritisch wird, schaltet man vielleicht lieber Compier-Optimierungen für diese Quelltextstelle ganz aus.
Ich bin ja so wie so der Meinung, dass die kürzeste Lösung die leicht verständlichste und beste ist. Und Zwischenvariable deklarieren plus zuweisen sind schon wieder zwei Zeilen mehr*, wobei man wahrscheinlich mit einem expliziten Cast das selbe Ergebnis erhält(?).

lg,
Daniel

*Es sei denn, es erhöht bei einer langen Rechnung das Verständnis, z.B. durch mehr Übersicht oder Benennung eines Zwischenergebnisses.

_________________
Centaur spears can block many spells, but no one tries to block if they see that the spell is a certain shade of green. For this purpose it is useful to know some green stunning hexes. (HPMoR)
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Fr 12.07.13 22:35 
Die Anzahl Zeilen im Sorcecode ist doch heutzutage mehr als nur irrelevant ;)

Ich würds "ausführlich" - und damit richtig - programmieren und nen Kommentar dazusetzen, wieso ich so schwafelig bin :lol:

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.