Delphi读取溢出标志

Delphi读取溢出标志,delphi,math,inline-assembly,basm,Delphi,Math,Inline Assembly,Basm,如果我这样做 var a,b,c:cardinal; begin a:=$80000000; b:=$80000000; c:=a+b; end; c将等于0,因为加法溢出。捕获此溢出布尔值的最佳方法是什么(a+b我也不是组装专家,但我认为这是可行的: 签名版本: function TryAdd(a, b: integer; out c: integer): boolean; asm ADD EAX, EDX // EAX := a + b; M

如果我这样做

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

c将等于0,因为加法溢出。捕获此溢出布尔值的最佳方法是什么<代码>(a+b我也不是组装专家,但我认为这是可行的:

签名版本:

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;
未签名版本:

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;

当然,使用方法A读取标志必须立即跟随相关运算符,因此该方法不太实用。相反,方法B采用高级语言结构化错误处理,因此推荐。

在汇编中,术语
溢出通常指有符号算术和平均数求和的符号不同于两个操作数的符号;对于无符号算术,术语
进位
更可取

您可以在纯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;

Andreas的纯pascal溶液(注释中建议的固定TryAdd)

函数TryAdd(a,b:整数;out c:整数):布尔值;重载;
变量
总和:int64;
开始
总和:=int64(a)+int64(b);
结果:=(低(整数)我讨厌汇编程序(完全不可移植),所以我使用溢出检查,如:

{$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}

有没有理由不在编译器选项中启用溢出检查和范围检查?我的最佳猜测是,他想知道是否存在溢出,但不会引发异常。我永远不会使用第一种方法。编译器可能会因为任何不明显的原因而覆盖标志(如果您重载了运算符等)。这可能会导致很难跟踪的问题。第二种解决方案对于任务来说有点太复杂了:如果定义$O+,使用后必须将编译状态重置为以前的$O?状态,因此一点也不方便。还有其他解决方案,它们更安全,也更快。@a.Bouchez,嗯,OP想了解一下由于方法A完美地展示了它是如何存在问题的——然后是方法B,它告诉codegenerator自动插入检查(基本相同,但有保证的正确位置)。顺便说一句,$O+有什么问题?如果你更改$optimization或$overflochecks状态,你最好保存它的状态,然后在完成后还原它。至少如果你希望干净的代码能够以所有状态编译。保存然后还原条件的状态是一项冗长的任务-请参阅Misha答案。以及设置globaly{$optimization off}这不是一个好主意。@a.Bouchez,请注意,$O和$Q都有局部范围。我不同意在这方面过于冗长是个好主意。无论如何,我只是演示了这个概念,没有任何“感染”的意图一些项目甚至是库。嗯…
$IOCHECKS
不是溢出检查。它是I/O检查;也许这就是为什么它被命名为
IOCHECKS
?:)您可能希望将其更改为
$Q
$OVERFLOWCHECKS
。保存然后恢复条件的状态是一项冗长的任务…如果您讨厌asm,则在Serg answer中有pascal版本。与所有这些条件技巧相比,这种方法更简洁。签名的代码有错误。测试:
TryAdd(-(MaxInt-1),-(MaxInt-1),c)
-未检测到溢出(两个负数之和为正数)。未签名代码效率低下。我怀疑这是最佳解决方案。Serg所说的都是真的。但这一切都取决于您实际尝试捕获的溢出以及您调用此Add…@gabr-Andreas代码(您正在模拟)的频率是正确的,但不是最佳的。@Serg的观点很好。虽然它很容易修复,但不是。我会在范围内测试
(总和,低(c),高(c))
。我对best的想法是最容易理解的。@David,@gabr-是的,它是有效的,尽管单元测试并不过分。有时我发现汇编指令更容易理解和使用。will
SETNC AL
SETNO AL
清除AH,如果EAX?@Stijn-否,则清除上半部分,它只设置一个字节。将值扩展为零对于32位,您还需要一条指令-
movzx eax,al
@Stijn…,Delphi不使用eax的更高位。设置al足以返回布尔值,因为只检查了al值。在Delphi生成的布尔代码上使用Alt-F2,您将看到这一点。
{$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}