这个问题的背景是......我正试图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)
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)
然而,更好的方法是使用已经计算出的所需结果,然后再使用此类技巧。