这个产生超大数字的 C 乘法的等效 Delphi 代码是什么?

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

这个问题的背景是...我正在尝试从Wikipedia: Hamming weight中移植C函数int popcount_3(uint64_t x),但是它的算法不是这个问题的重点。

假设x是无符号64位值的最大个数(即x = 18446744073709551615),在计算结束时,C代码将计算:

uint64_t iResult = 578721382704613384ull * 72340172838076673ull;

首先我怀疑C代码引发了溢出错误,因为使用浮点乘法运算时的actual/real结果等于41864804849942400000000000000000000,但实际上,该C代码没有编译错误。输出为 4627501566018457608.

我们知道,上述 C 代码的 Delphi 代码为:

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

Delphi 编译器引发错误 E2099 转换或算术运算溢出。好的,我看到错误是合理的。

所以,我的问题是...什么是等效的 Delphi 大数乘法代码,使得 Delphi 给出与 C 中相同的结果?

稍后添加

为了预测即将到来的潜在问题"Why did I provide the example using a true constant expression?",原因是我想创建一个真正的常量来计算通过计算 High(NativeUInt).

中设置的位数来计算 computer word 中的位数

重现步骤

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 64 位 XE3 Windows 编译器模式

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;

Delphi 编译器尝试在编译时计算常量表达式 UInt64(578721382704613384) * UInt64(72340172838076673) 并向您报告溢出错误。

解决方案是使用变量:

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

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

这段代码产生了预期的结果。

请注意,项目选项中的 Overflow check 标志必须关闭。或者在代码中使用编译器指令,例如

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

编辑 基于@hvd 的警告

这些指令只有在项目选项 Overflow checkOn 的情况下才有意义。如果没有,可以省略这些指令。

第三种方式,最常见和通用,是使用 {$ifopt ...} 指令(@hvd,再次感谢):

{$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}

然而,更好的方法是使用已经计算出的期望结果,然后使用这些技巧。