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

Aeo*_*yan 6 delphi delphi-xe3

这个问题的背景是......我正试图int popcount_3(uint64_t x)维基百科中移植C函数:汉明重量,但它的算法不是这个问题的焦点.

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

uint64_t iResult = 578721382704613384ull * 72340172838076673ull;
Run Code Online (Sandbox Code Playgroud)

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

我们知道,上面C代码的Delphi代码是:

iResult := UInt64(578721382704613384) * UInt64(72340172838076673);
Run Code Online (Sandbox Code Playgroud)

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

所以,我的问题是......对于超大数字的乘法,等效的Delphi代码是什么,以便Delphi给出与C相同的结果?

稍后补充

预测即将发生的潜在问题"为什么我使用真正的常量表达式提供示例?" ,原因是我想创建一个真常量,通过计算设置位数来计算计算机字中的位数High(NativeUInt).

重现步骤

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;
}
Run Code Online (Sandbox Code Playgroud)

Delphi XE3采用64位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;
Run Code Online (Sandbox Code Playgroud)

Abe*_*sto 3

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;
Run Code Online (Sandbox Code Playgroud)

此代码产生所需的结果。

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

  {$OVERFLOWCHECKS OFF}
  iResult := i1 * i2;
  {$OVERFLOWCHECKS ON}
Run Code Online (Sandbox Code Playgroud)

根据@hvd的警告进行编辑

这些指令仅在项目选项为 的情况下才有Overflow check意义On。如果不是,则可以省略这些指令。

第三种方法,最常见和通用的,是使用 {$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}
Run Code Online (Sandbox Code Playgroud)

然而,更好的方法是使用已经计算出的所需结果,然后再使用此类技巧。

  • 警告:如果项目设置中的 OVERFLOWCHECKS 处于关闭状态,则编译器指令会针对文件的其余部分将其打开。为了尊重文件其余部分的项目设置,您需要“$IFOPT”来确定“OVERFLOWCHECKS”是否已打开,并且仅在之前打开时才将其重新打开。 (3认同)