Delphi XE 中奇怪的 SHL 运算

5

v:=v shl b有什么问题?我试图计算mask=2n-1,像mask:=1 shl n-1一样,但对于整数变量n=64失败了。

program UInt64Test;

{$APPTYPE CONSOLE}

var
  u,v,w:uint64;
const
  a=64;
var
  b:integer=a;
  c:integer=a-1;

begin
  u:=1; v:=1; w:=1;
  u:=u shl a;
  v:=v shl b;
  w:=w shl 1 shl c;
  writeln(u);
  writeln(v);
  writeln(w);
  readln;
end.

输出:

0
1
0

我也怀疑 v 是零。

解决方法为 2 shl (n-1)-1。在这种情况下,编译器执行机器的 shl(而不是 __llshl):

function reciprocal(o:uint64;n:byte=64):uint64; // result * o = 1 (mod 2ⁿ)
var
  b,m,t:uint64;
begin
  result:=0;
  t:=2 shl (n-1)-1;
  m:=0; b:=1;
  while b<>0 do begin
    m:=m or b;
    if ((o*result) and m)<>1 then result:=result or b;
    b:=(b shl 1) and t;
  end;
end;

...但是,我不开心。


1
尝试使用 v := v shl UInt64(b); - valex
1
进入__llshl函数后,发现b被掩码为$3F。这意味着移位操作是用零完成的。结果将为1。 - LU RD
@valex,这也会得到1。 - LU RD
1
直到D2010,__llshl对于n=64执行移位操作->结果为0,这与机器指令不同。现在通过掩码$3F进行了修复,但Delphi优化器中仍然执行移位操作,例如u := u shl a并且仍然分配0。 - pf1957
1
@Jan Doggen:也许有一天,因为我还没有使用XE3,目前也没时间升级它。 - pf1957
显示剩余5条评论
1个回答

11
根据文档中的说明:
操作符 x shl y 和 x shr y 将 x 的值左移或右移 y 位,如果 x 是无符号整数,则相当于将其乘以或除以 2^y;结果的类型与 x 相同。例如,如果 N 存储值 01101(十进制数为 13),则 N shl 1 返回值 11010(十进制数为 26)。请注意,y 的值被解释为 x 类型大小的模数。例如,如果 x 是整数,x shl 40 被解释为 x shl 8,因为整数是 32 位,40 mod 32 为 8。
因此,在 64 位值上执行 1 shl 64 的操作被解释为 1 shl 0,即 1。
const
  aa = 32;
var
  x,y,z : Cardinal;
...
x := 1;
y := 32;
z := x shl aa; // Gives z = 1
z := x shl 32; // Gives z = 1
z := x shl y;  // Gives z = 1;

看起来当y是一个常量时,64位值存在编译器错误。

请注意,在64位模式下,1 shl 64的结果为1。

所以这个bug只存在于32位编译器中。

已报告为QC112261 SHL operations by constant fails


如果您希望移位操作的结果对于y值>= 64为0,则可以使用此函数:

function ShiftLeft( AValue : UInt64; bits : Integer) : UInt64; inline;
begin
  if (bits > 63) then 
    Result := 0 // Avoid bits being modified modulo 64
  else
    Result := AValue shl bits;
end; 

更新

此编译器错误在XE4版本中已得到解决。


2
这解释了故事的一半。但是为什么编译器在编译时评估常量表达式时不使用相同的规则?在XE3中,它们仍然会出错吗?32位和64位编译器都是如此吗? - David Heffernan
1
32位编译器(XE)如果移位常量超出范围则会在编译时生成错误,但不会生成运行时范围检查:`{$R+} var J: Integer; I: Integer;begin I:= 1; J:= 40; Writeln(I, I shl J); // 不会生成范围错误 readln; end.` - kludg
1
@Leif RD代表什么?对于答案和QC报告+1。干得好。 - David Heffernan
2
@DavidHeffernan,我是公司的研发经理。我的工作涉及光学/机械结构以及建立气体和颗粒监测系统的软件工程领域。激光器/干涉仪/光谱仪是我日常工作的主要内容。我们的软件集成了大量的人年,用于数据收集/展示,与GIS模型气象预报/现在预报相结合。 - LU RD
@AndriyM,这意味着它已经通过了他们的内部检查,并将在下一个更新中纳入,希望是XE3更新3。虽然我从未见过如此快速的修复。 - LU RD
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接