3

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?)

2
  • Under further investigation it seems that there is a bug in __llmulo (system.pas). Commented Jul 5, 2012 at 13:43
  • I have found a (sort of) workaround here: qc.embarcadero.com/wc/qcmain.aspx?d=34049 and I can use something similar Fastcode to patch the __llmulo. The problem is that the workaround code may contain bugs (see comments further down in QA page). Does anybody have a testes/working __llmulo? Commented Jul 5, 2012 at 20:21

2 Answers 2

3

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;
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks Paul. It's difficult to replace all * with a call to the Multiplication function above. I'm looking for a more global solution, maybe something that would patch __llmulo (the system.pas function called to do the Int64 multiplication), something similar to Fastcode.
Yes, that would be much easier! As an FYI, I am using Delphi XE2 and I can still duplicate this bug. Of course it's 'closed' as being fixed in D2005... So "Upgrade your Delphi" is not an answer either.
Hi Paul - For curiosity, could you check in XE2 if the __llmulo procedure (in system.pas) still says (in the comments above the procedure) "64-bit signed multiply, with overflow check (98.05.15: overflow not supported yet)" and if the code is the same as the __llmul procedure?
Yes, the same comment is there. The code between those two procedures is in fact 100% identical.
Note that QualityCentral has now been shut down, so you can't access qc.embarcadero.com links anymore. If you need access to old QC data, look at QCScraper.
0

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;

1 Comment

From Review: A link to a solution is welcome, but please ensure your answer is useful without it: add context around the link so your fellow users will have some idea what it is and why it’s there, then quote the most relevant part of the page you're linking to in case the target page is unavailable. Answers that are little more than a link may be deleted. See: How do I write a good answer?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.