Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Überlauf bei Rechenoperation mitbekommen (https://www.delphipraxis.net/179823-ueberlauf-bei-rechenoperation-mitbekommen.html)

Popov 4. Apr 2014 12:24

Überlauf bei Rechenoperation mitbekommen
 
Ich hab folgendes Problem, ich möchte den Überlauf bei einer Integer Rechenoperation mitbekomme, allerdings ohne dass es eine Fehlermeldung gibt. Ich will es einfach nur wissen. Die Rechenoperation soll wie üblich fortgesetzt werden, mit dem falschen Ergebnis, ich will es nur wissen.

Eine Lösung habe ist, aber die gefällt mir nicht. Standard ist {$Q-}. Mit der Direktive {$Q+} kann ich die Überlaufprüfung einschalten, allerdings gibt es dann eine Fehlermeldung. Die kann ich mit EIntOverflow abfangen, allerdings wird die Rechnung nicht fortgeführt, aber das will ich. Eine Möglichkeit ist die Info zu kassieren und dann ein weiteres Mal, dieses mit {$Q-} zu rechnen. Aber so blöd ist der Compiler nicht. Der sagt sich - der Typ weiß nicht was er will - und ignoriert eine von beiden Routinen. Man kann anscheinend in einer Prozedur nicht gleichzeitig {$Q+} und {$Q-} nutzen. Also muss eine der Routinen ausgelagert werden. Und spätestens hier wird aus eine Vierzeilener ein Zwanzigzeiler.

Gibt es eine Möglichkeit bei {$Q-} nach einer Rechnung mitzubekommen ob es einen Überlauf gab?

himitsu 4. Apr 2014 12:42

AW: Überlauf bei Rechenoperation mitbekommen
 
Ich wüsste jetzt nichts, außer Try-Except und im Except nochmals berechnen, ohne Überlaufprüfung.

Delphi-Quellcode:
// A := B * C; mit {$Q-}
mov eax,[ebp-$14]
imul dword ptr [ebp-$18]
mov [ebp-$10],eax

//A := B * C; mit {$Q+}
mov eax,[ebp-$14]
imul dword ptr [ebp-$18]
jno $0dde2376
call $0dd71098
mov [ebp-$10],eax

Dejan Vu 4. Apr 2014 12:43

AW: Überlauf bei Rechenoperation mitbekommen
 
Natürlich: Die Operation einfach mit Int64 Werten ausprobieren.

Mein Tipp: Mach aus dem 4-Zeiler einen 20-Zeiler, denn mehr Zeilen heißt nicht gleich 'weniger Übersichtlich'. Was Du hier vorhast ist ja auch etwas merkwürdig.

Übrigens wird der 'Trick' mti den Int64-Werten bei der 'MulDiv' Funktion verwendet. Hier wird a*b div c gerechnet, aber so, das ein Überlauf (bei a*b) vermieden wird.

gammatester 4. Apr 2014 12:47

AW: Überlauf bei Rechenoperation mitbekommen
 
Das hängt von den verwendeten Operationen und Datentypen ab. Ohne ASM ist es im allgemeinen schwierig. Ich verwende zB für den für mich interessanten Fall der Überlauferkennung bei signed 32-Bit-Addition:

Delphi-Quellcode:
function add32_ovr(x,y: longint; var z: longint): boolean;
  {-Add z=x+y with overflow detection}
begin
  {See Hacker's delight, Ch 2-12}
  {$ifopt Q+}
    z := longint(int64(x)+y);
  {$else}
    z := x+y;
  {$endif}
  add32_ovr := ((z xor x) and (z xor y)) < 0;
end;
Hinweise für einige Operationen bei http://www.hackersdelight.org/ oder http://graphics.stanford.edu/~seander/bithacks.html

Neutral General 4. Apr 2014 12:55

AW: Überlauf bei Rechenoperation mitbekommen
 
Was bei mir zumindest für Integer Operationen funktioniert ist folgendes:
Delphi-Quellcode:
function OverflowHappend(): Boolean;
asm
  jno @no_overflow
  mov eax, 1
  ret
@no_overflow:
  xor eax, eax
end;

procedure TForm1.Button1Click(Sender: TObject);
var n: Integer;
begin
  n := MaxInt;
  n := n + 40;
  if OverflowHappend then
    ShowMessage('Overflow');

  Caption := IntToStr(n); // Damit Delphi da nichts wegoptimiert
end;

Popov 4. Apr 2014 13:37

AW: Überlauf bei Rechenoperation mitbekommen
 
Zitat:

Zitat von Dejan Vu (Beitrag 1254644)
Was Du hier vorhast ist ja auch etwas merkwürdig.

Stimmt. Aber in dem speziellen Fall ist das "übliche" Verhalten gewollt (es ist ja auch die Standardeinstellung). Nur will ich es mitbekommen.

Das mit einem Int64 (bzw. allgemein größeren Zahl) ist es auch möglich (man prüft mit if ob Max überschritten wurde), ich hatte aber fixe Idee die Rechnung normal auszuführen und die Info vom System zu erhalten.

Das "Problem" ist, dass das eigentlich einfach zu lösen ist, also ein paar Dummy-Variablen usw. Nur so dumm ist der Compiler nicht. Wenn er er glaubt, dass eine Rechnung nicht nötig ist, und prüfen auf Error ist eigentlich unsinnig, dann ignoriert er die Routine.

//EDIT:

Also das kürzeste was ich hin bekomme (hier im Beispiel mit Byte) ist das:
Delphi-Quellcode:
function IncByte(var X: Byte; N: Byte = 1): Boolean;
  function IncTest: Boolean;
  begin
    Result := False;
    {$OverFlowChecks On}
    try
      Inc(X, N);
    except
      on EIntOverflow do
        Result := True;
    end;
  end;
var
  X2: Byte;
begin
  X2 := X;
  Result := IncTest;

  {$OverFlowChecks Off}
  if Result then
  begin
    X := X2;
    Inc(X, N);
  end;
end;
Es müssen zwei Funktionen sein, sonst klappt das nicht.

himitsu 4. Apr 2014 14:46

AW: Überlauf bei Rechenoperation mitbekommen
 
Wieso klappt das denn nicht?

$Q, bzw. $OverFlowChecks sollte nur auf Zeilenebene gültig sein, alss nur bis zur nächsten Definition, oder bis Dateiende, falls nichts mehr kommt.



Bei INC muß man etwas aufpassen ... siehe Beschreibung vom letzten Beispiel und darum hier auch die Tempvariable.
Delphi-Quellcode:
function IncByte(var X: Byte; N: Byte = 1): Boolean;
var
  X2: Byte;
begin
  X2 := X;
  try
    {$OverFlowChecks On}
    Inc(X, N);
    {$OverFlowChecks Off}  // entweder das
    Result := True;
  except
    on EIntOverflow do begin
      X := X2;
      {$OverFlowChecks Off}  // oder das .... das wo, sollte nahezu egal sein
      Inc(X, N);
      Result := False;
    end;
  end;
end;


Wenn es vor dem Zuweisen knallt, dann geht das auch ohne zusätzliche Variable.
Delphi-Quellcode:
function IncByte(var X: Byte; N: Byte = 1): Boolean;
begin
  try
    {$OverFlowChecks On}
    X := X + N;
    Result := True;
  except
    on EIntOverflow do begin
      {$OverFlowChecks Off}
      Inc(X, N);
      Result := False;
    end;
  end;
end;
Wenn INC direkt im Ziel-Speicher rechnet, dann ist auch mit Exception dennoch das Ergebnis bereits in der Variable und muß nicht nochmal berechnet werden.
Delphi-Quellcode:
function IncByte(var X: Byte; N: Byte = 1): Boolean;
begin
  try
    {$OverFlowChecks On}
    Inc(X, N);
    {$OverFlowChecks Off}
    Result := True;
  except
    on EIntOverflow do
      Result := False;
  end;
end;

Popov 4. Apr 2014 21:51

AW: Überlauf bei Rechenoperation mitbekommen
 
Also ich weiß nicht wie das in höheren Delphi Versionen ist, aber
Delphi-Quellcode:
begin
  {$Q+}
  ...
  {$Q-}
end;
beide Direktiven in einer Funktion klappt nicht bei mir. Wenn am Ende {$Q-} steht, hat bei mir {$Q+} am Anfang keine Wirkung. Es gibt bei mir keine Fehlermeldung.

Was bleib ist es auf zwei Funktionen zu verteilen:
Delphi-Quellcode:
function IncByte(var X: Byte; N: Byte = 1): Boolean;
  procedure IncTest;
  begin
    {$OverFlowChecks On}
    Inc(X, N);
  end;
var
  X2: Byte;
begin
  X2 := X;
  try
    IncTest;
    {$OverFlowChecks Off}
    Result := False;
  except
    on EIntOverflow do
    begin
      {$OverFlowChecks Off}
      X := X2;
      Inc(X, N);
      Result := True;
    end;
  end;
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:06 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