6
votes

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;
1
It's rather galling that the compiler can't evaluate this as a true constant.David Heffernan
It's hard to believe they overlooked such a trivial thing, considering the age of Delphi.Aeoliyan
@DavidHeffernan - Just to clarify, what I meant as "they" is Embarcadero.Aeoliyan

1 Answers

3
votes

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.