What is the equivalent Delphi code to this C multiplication that yields an oversized number?
Asked Answered
T

1

6

The background for this question is ... I'm trying to port the C function int popcount_3(uint64_t x) from the Wikipedia: Hamming weight, but its algorithm is not the focus of this question.

Let's say x is the maximum number of unsigned 64-bit value (that is x = 18446744073709551615), at the end of calculation, the C code will calculate:

uint64_t iResult = 578721382704613384ull * 72340172838076673ull;

Firstly I suspected the C code raised overflow error because the actual/real result of the operation when using floating-point multiplication equals 41864804849942400000000000000000000, but in fact, no compiling error for that C code. The output is 4627501566018457608.

As we know, the Delphi code for the above C code would be:

iResult := UInt64(578721382704613384) * UInt64(72340172838076673);

The Delphi compiler raise an error E2099 Overflow in conversion or arithmetic operation. OK, I see the error is reasonable.

So, my question is ... What is the equivalent Delphi code for the multiplication of oversized numbers such that Delphi gives the same result as in C?

Added later

To anticipate a coming potential question "Why did I provide the example using a true constant expression?", the reason is that I want to create a true constant that calculates the number of bits in the computer word by counting the number of set bits in High(NativeUInt).

Steps to Reproduce

GCC 4.8.1 (MinGW)

#include <stdio.h>
#include <stdint.h>

int main () {
  uint64_t iResult = 578721382704613384ull * 72340172838076673ull;
  printf("iResult = %llu\n", iResult); // output --> 4627501566018457608
  return 0;
}

Delphi XE3 in 64-bit Windows compiler mode

procedure Test;
var
  iResult   : UInt64;
  RealResult: Double;

begin
  iResult := UInt64(578721382704613384) * UInt64(72340172838076673);
  RealResult := 578721382704613384.0 * 72340172838076673.0;
  WriteLn('iResult    = ', iResult);    // error  --> E2099 Overflow in ...
  WriteLn('RealResult = ', RealResult); // output --> 4.18648048499424E+0034
end;
Thereupon answered 5/9, 2015 at 8:10 Comment(3)
It's rather galling that the compiler can't evaluate this as a true constant.Enceladus
It's hard to believe they overlooked such a trivial thing, considering the age of Delphi.Thereupon
@DavidHeffernan - Just to clarify, what I meant as "they" is Embarcadero.Thereupon
D
3

Delphi compiler tries to calculate constant expression UInt64(578721382704613384) * UInt64(72340172838076673) at compile time and reports about overflow error to you.

The solution is using variables:

var
  iResult, i1, i2   : UInt64;
  RealResult: Double;

begin
  i1 := 578721382704613384;
  i2 := 72340172838076673;
  iResult := i1 * i2;
  RealResult := 578721382704613384.0 * 72340172838076673.0;

This code produces desired result.

Note that Overflow check flag in the project options must be off. Or use compiler directives in the code like

  {$OVERFLOWCHECKS OFF}
  iResult := i1 * i2;
  {$OVERFLOWCHECKS ON}

Edit Based on @hvd's warning

Those directives have sense only in case if in a project's options Overflow check is On. If not, those directives can be omitted.

The third way, most commonly and universal, is using {$ifopt ...} directive (@hvd, thanks again):

{$ifopt Q+} // If option is On ...
  {$Q-} // then turn it Off ...
  {$define TURNQON} // and keep in mind that it must be restored
{$endif} 
iResult := i1 * i2;
{$ifdef TURNQON}{$Q+}{$undef TURNQON}{$endif}

However the better way is using already calculated desired result then such tricks.

Diagnosis answered 5/9, 2015 at 8:28 Comment(8)
Warning: if OVERFLOWCHECKS is off in the project settings, then your compiler directives turn it on for the rest of the file. To respect the project settings for the rest of the file, you need $IFOPT to determine whether OVERFLOWCHECKS was on, and only turn it back on if it was on before.Dirndl
@Diagnosis - Precalculating the desired result in this case is not possible beause the desired result depends on value of x passed to the int popcount_3(uint64_t x). Although your variable utilization trick doesn't work for my case as described in my question (see my update), your trick is still useful for me. It seems there is no other way to solve my problem. Since it was my mistake that did not describe the problem comprehensively, I accept your answer anyway. Cheers :-)Thereupon
@Thereupon I don't understand that comment. If the operands are only known at runtime, then you can't use a constant at all.Enceladus
@David Heffernan - No, the operand in my case can be known in compile time, that is High(NativeUInt). I have successfully get the number of bits in the computer word using int popcount_2 function instead of int popcount_3. I did it in const section as true constant.Thereupon
const __ciMaxCpuWord = UInt64(High(NativeUInt)); __ciC06 = UInt64(__ciMaxCpuWord - ((__ciMaxCpuWord shr 1) and UInt64($5555555555555555))); __ciC07 = UInt64((__ciC06 and UInt64($3333333333333333)) + ((__ciC06 shr 2) and UInt64($3333333333333333))); __ciC08 = UInt64( (__ciC07 + (__ciC07 shr 4)) and UInt64($0F0F0F0F0F0F0F0F) ); __ciC09 = UInt64(__ciC08 + (__ciC08 shr 8)); __ciC10 = UInt64(__ciC09 + (__ciC09 shr 16)); __ciC11 = UInt64(__ciC10 + (__ciC10 shr 32)); ciNumBitsInCpuWord = __ciC11 and UInt64($000000000000007F);Thereupon
I can't make any sense of this. All the same, this is the answer to the question you asked so you were right to accept it.Enceladus
@DavidHeffernan - I think doing this is simpler than providing the constant for every platform. We already can get the size of NativeUInt by the true constant SizeOf(NativeUInt) whose the size is platform-dependent, while my ciNumBitsInCpuWord will get the number of bits in NativeUInt. The answer force me to use function utilizing variables instead of precalculated true constant, and I has admit it was my mistake on the first place since I didn't tell that I wanted to calculate it in const section. So, I accept it.Thereupon
You can just pre-calculate these constants. It's that simple.Enceladus

© 2022 - 2024 — McMap. All rights reserved.