3

在 Delphi 中,如何检测溢出错误Int64

对于整数,我们可以这样做:

type
MyInt = Integer; //Int64

function TryMaxTimes10(out Res: MyInt): boolean;
var
  a, b: MyInt;
begin
  {$Q+}
  try
    a := High(MyInt);
    b := 10;
    Res := a * b; //REF1
    Result := True; 
  except
    Result := False;
  end;
  {$Q-}
end;

对于MyInt = Integer, REF1 行给出了一个异常,因此TryMaxTimes10返回false

但是如果我们将 MyInt 更改为MyInt = Int64REF1 则不会给出异常并TryMaxTimes10返回true

据我所知,帮助{$Q+}没有特别提到Int64{$Q+} state, certain integer arithmetic operations ... are checked for overflow...。

问题:所以我的问题是,我们如何检测 Int64 的溢出错误?

(我使用的是 Delphi 7。在较新版本的 Delphi 中也会发生同样的事情吗?)

4

2 回答 2

3

这是一个已知的问题。请参阅http://qc.embarcadero.com/wc/qcmain.aspx?d=10185以及 Andy 在底部写的评论。

我的建议是创建一个函数(我没有编译或测试这个 - 只是一个例子):

function Foo(A, B : Int64) : Int64;
var bNeg : boolean;
begin
  // Do we expect a negative result?
  bNeg := ((a < 0) xor (b < 0));
  // Get the real result
  Result := a * b;
  // If the result is wrong, raise an error
  if ((Result < 0) xor bNeg) then begin
    // Raise EOverFlow
  end;
end;
于 2012-07-05T12:40:10.177 回答
0

此错误已在 RAD Studio 10.2 Tokyo 中修复。该问题可以在这里找到(但必须使用 embarcadero 帐户登录才能看到它)。

这是 John O'Harrow 的 __llmulo 的正确版本(在 MPL 1.1 下获得许可)随 Delphi 10.2 及更高版本一起提供:

//  Param 1(edx:eax), Param 2([esp+8]:[esp+4])
//  Result is stored in edx:eax
//  O-flag set on exit   => result is invalid
//  O-flag clear on exit => result is valid
procedure __llmulo();
asm
  test    edx, edx           {Param1-Hi = 0?}
  jne     @@Large            {No, More than one multiply may be needed}
  cmp     edx, [esp+8]       {Param2-Hi = 0?}
  jne     @@Large            {No, More than one multiply may be needed}
  mul     dword ptr [esp+4]  {Only one multiply needed, Set Result}
  and     eax, eax           {Clear Overflow Flag}
  ret     8
@@Large:
  sub     esp, 28            {allocate local storage}
  mov     [esp], ebx         {save used registers}
  mov     [esp+4], esi
  mov     [esp+8], edi
  mov     [esp+12], ebp
  mov     ebx, [esp+32]      {Param2-Lo}
  mov     ecx, [esp+36]      {Param2-Hi}
  mov     esi, edx
  mov     edi, ecx
  sar     esi, 31
  sar     edi, 31
  xor     eax, esi
  xor     edx, esi
  sub     eax, esi
  sbb     edx, esi           {edx:eax (a1:a0) = abs(Param1)}
  xor     ebx, edi
  xor     ecx, edi
  sub     ebx, edi
  sbb     ecx, edi           {ecx:ebx (b1:b0) = abs(Param2)}
  xor     esi, edi           {Sign Flag, 0 if Params have same sign else -1}
  mov     [esp+16], eax      {a0}
  mov     [esp+20], edx      {a1}
  mov     [esp+24], ecx      {b1}
  mul     ebx                {edx:eax (c1:c0) = a0*b0}
  xchg    ebx, edx           {ebx = c1, edx = b0}
  mov     edi, eax           {abs(Result-Lo) = c0}
  xor     ecx, ecx           {Upper 32 bits of 128 bit result}
  xor     ebp, ebp           {Second 32 bits of 128 bit result}
  mov     eax, [esp+20]      {a1}
  mul     edx                {edx:eax (d1:d0) = a1*b0}
  add     ebx, eax           {c1 + d0}
  adc     ebp, edx           {d1 + carry}
  adc     ecx, 0             {Possible carry into Upper 32 bits}
  mov     eax, [esp+16]      {a0}
  mov     edx, [esp+24]      {b1}
  mul     edx                {edx:eax (e1:e0) = a0*b1}
  add     ebx, eax           {abs(Result-Hi) = c1 + d0 + e0}
  adc     ebp, edx           {d1 + e1 + carry}
  adc     ecx, 0             {Possible carry into Upper 32 bits}
  mov     eax, [esp+20]      {a1}
  mov     edx, [esp+24]      {b1}
  mul     edx                {edx:eax (f1:f0) = a1*b1}
  add     ebp, eax           {d1 + e1 + f0 + carry}
  adc     ecx, edx           {f1 + carry}
  or      ecx, ebp           {Overflow if ecx <> 0 or ebp <> 0}
  jnz     @@Overflow
  mov     edx, ebx           {Set abs(Result-Hi)}
  mov     eax, edi           {Set abs(Result-Lo)}
  cmp     edx, $80000000
  jae     @@CheckRange       {Possible Overflow if edx>=$80000000}
@@SetSign:
  xor     eax, esi           {Correct Sign of Result}
  xor     edx, esi
  sub     eax, esi
  sbb     edx, esi
  mov     ebx, [esp]         {restore used registers}
  mov     esi, [esp+4]
  mov     edi, [esp+8]
  mov     ebp, [esp+12]
  add     esp, 28            {Clears Overflow flag}
  ret     8
@@CheckRange:
  jne     @@Overflow         {Overflow if edx>$80000000}
  test    esi, esi           {edx=$80000000, Is Sign Flag=0?}
  jnz     @@SetSign          {No, Result is Ok (-MaxInt64)}
@@Overflow:
  mov     ebx, [esp]         {restore used registers}
  mov     esi, [esp+4]
  mov     edi, [esp+8]
  mov     ebp, [esp+12]
  add     esp, 28
  mov     ecx, $80000000
  dec     ecx                {Set Overflow Flag}
  ret     8
end;
于 2019-10-24T09:48:48.540 回答