开发者

Delphi read overflow flag

If I do this

var
  a,b,c:cardinal;
begin
  a:=$80000000;
  b:=$80000000;
  c:=a+b;
end;

c will开发者_运维问答 equal 0, since the addition overflowed. What's the best way to catch this overflowed boolean? (a+b<a) or (a+b<b)? a really nice way would be with inline assembler, but I'm not that prolific in assembler (though my guess would be it would envolve something like JO)


In assembly the term Overflow usually refers to signed arithmetic and means that the sign of the sum is different from the signs of both operands; for unsigned arithmetic the term Carry is preferable.

You can implement addition with Overflow (Carry) check in pure pascal:

// signed add - returns True if no overflow produced
function SAdd(A, B: integer; out C: integer): Boolean;
begin
  C:= A + B;
  Result:= (A xor B < 0)   // operands have different signs
        or (C xor A >= 0); // sum has the same sign as operands
end;

// unsigned add - returns True if no carry produced
function UAdd(A, B: Cardinal; out C: Cardinal): Boolean;
begin
  C:= A + B;
  Result:= (C >= A);
end;

The same functions in assembly - optimized variant of Andreas' solution:

// Signed Add
function SAdd(A, B: Integer; out C: Integer): Boolean;
asm
        ADD   EAX,EDX
        MOV   [ECX],EAX
        SETNO AL
end;

// Unsigned Add
function UAdd(A, B: Cardinal; out C: Cardinal): Boolean;
asm
        ADD   EAX,EDX
        MOV   [ECX],EAX
        SETNC AL
end;


I am not an expert on assembly either, but I think this works:

Signed version:

function TryAdd(a, b: integer; out c: integer): boolean;
asm
  ADD EAX, EDX             // EAX := a + b;
  MOV [c], EAX             // c := EAX;
  JO @@END                 // if overflow goto end;
  MOV EAX, true            // result := true
  RET                      // Exit;
@@END:
  XOR EAX, EAX             // result := false;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  c: integer;
begin
  if TryAdd(MaxInt - 5, 6, c) then
    ShowMessage(IntToHex(c, 8))
  else
    ShowMessage('Overflowed!');
end;

Unsigned version:

function TryAdd(a, b: cardinal; out c: cardinal): boolean;
asm
  ADD EAX, EDX             // EAX := a + b;
  MOV [c], EAX             // c := EAX;
  JC @@END                 // if overflow goto end;
  MOV EAX, true            // result := true
  RET                      // Exit;
@@END:
  XOR EAX, EAX             // result := false;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  c: cardinal;
begin
  if TryAdd($A0000000, $C0000000, c) then
    ShowMessage(IntToHex(c, 8))
  else
    ShowMessage('Overflowed!');
end;


Andreas' solution in pure pascal (with fixed TryAdd as suggested in the comments).

function TryAdd(a, b: integer; out c: integer): boolean; overload;
var
  sum: int64;
begin
  sum := int64(a) + int64(b);
  Result := (Low(integer) <= sum) and (sum <= High(integer));
  c := integer(Int64Rec(sum).Lo);
end;

function TryAdd(a, b: cardinal; out c: cardinal): boolean; overload;
var
  sum: int64;
begin
  sum := int64(a) + int64(b);
  Result := sum <= High(cardinal);
  c := Int64Rec(sum).Lo;
end;

procedure TForm32.Button1Click(Sender: TObject);
var
  c: integer;
begin
  if TryAdd(MaxInt - 5, 6, c) then
    ShowMessage(IntToHex(c, 8))
  else
    ShowMessage('Overflowed!');
end;


{$OPTIMIZATION OFF}
procedure TForm1.FormCreate(Sender: TObject);

  function Overflow(): WordBool;
  const
    fOverflow   = $0800;
  asm
      PUSHF
      POP   AX
      AND   AX, fOverflow;
  end;

var
  I, J, K: Integer;
begin
  I := $80000000;
  J := $80000000;

  { method A - read FLAGS register }
  {$OVERFLOWCHECKS OFF}
  K := I + J;
  if Overflow() then Windows.Beep(5000, 50);

  { method B - have compiler to generate check and catch an exception }
  {$OVERFLOWCHECKS ON}
  try
    K := I + J;
  except on E: EIntOverflow do
    ShowMessage('OH SHI-');
  end;

end;

Naturally, reading FLAGS using Method A must immediately follow an operator in question, thus it makes this method not very practical. In contrast, method B employs High Level Language structured error handling and thus - recommended.


I detest assembler (completey non-portable) so I use overflow checking like:

{$IFOPT Q-}
{$DEFINE CSI_OVERFLOWCHECKS_OFF}
{$ENDIF}
{$OVERFLOWCHECKS ON}

a:=$80000000;
b:=$80000000;
c:=a+b;

{$IFDEF CSI_OVERFLOWCHECKS_OFF}
{$UNDEF CSI_OVERFLOWCHECKS_OFF}
{$OVERFLOWCHECKS OFF}
{$ENDIF}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜