8

私がこれをしたら

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

加算がオーバーフローしたため、c は 0 になります。このオーバーフローしたブール値をキャッチする最良の方法は何ですか? (a+b<a) or (a+b<b)? 本当に良い方法はインラインアセンブラを使用することですが、私はアセンブラでそれほど多作ではありません(私の推測では、次のようなものが含まれると思いますがJO

4

5 に答える 5

8

アセンブリでは、この用語Overflowは通常符号付き算術演算を指し、合計の符号が両方のオペランドの符号と異なることを意味します。符号なし算術演算では、この用語Carryが適しています。

純粋なパスカルでオーバーフロー (キャリー) チェックを使用して加算を実装できます。

// 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;

アセンブリの同じ機能 - アンドレアスのソリューションの最適化されたバリアント:

// 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;
于 2011-06-21T03:10:18.640 に答える
5

純粋なパスカルでのAndreasのソリューション(コメントで提案されているようにTryAddを修正)。

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;
于 2011-06-21T07:27:10.640 に答える
5

私も組み立ての専門家ではありませんが、これはうまくいくと思います。

署名されたバージョン:

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;

署名されていないバージョン:

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;
于 2011-06-20T23:06:08.793 に答える
0

私はアセンブラが嫌いなので(完全に移植性がありません)、次のようなオーバーフロー チェックを使用します。

{$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}
于 2011-06-21T07:47:52.763 に答える
0
{$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;

当然、方法 A を使用して FLAGS を読み取るには、問題の演算子の直後に続く必要があるため、この方法はあまり実用的ではありません。対照的に、方法 B は高水準言語の構造化されたエラー処理を採用しているため、推奨されます。

于 2011-06-21T01:12:48.817 に答える