Delphi - Detect Int64 Overflow Error

你。 提交于 2020-02-24 09:10:46

问题


In Delphi how can I detect overflow errors for Int64?

For Integers we could do:

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;

For MyInt = Integer, line REF1 gives an exception and so TryMaxTimes10 returns false.

But if we change MyInt to MyInt = Int64, then REF1 does not give an exception and TryMaxTimes10 returns true!

I understand that the help for {$Q+} does not specifically mention Int64: ... {$Q+} state, certain integer arithmetic operations ... are checked for overflow.

QUESTION: So my question is, how can we detect overflow errors for Int64?

(I'm using Delphi 7. Does the same thing happen in newer versions of Delphi?)


回答1:


This is a known issue. See http://qc.embarcadero.com/wc/qcmain.aspx?d=10185, and the comments Andy wrote at the bottom.

My suggestion would be to create a function (I did not compile nor test this - just an example):

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;



回答2:


This bug has been fixed in RAD Studio 10.2 Tokyo. The issue can be found here (but one have to log in with embarcadero account to see it).

Here is correct version of __llmulo by John O'Harrow (licensed under MPL 1.1) shipped with Delphi versions 10.2 and above:

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


来源:https://stackoverflow.com/questions/11344263/delphi-detect-int64-overflow-error

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!