Delphi XE中的奇怪SHL操作

Srb*_*rba 5 delphi bit-shift

怎么了v:=v shl b?我正在尝试计算mask = 2 n -1 like 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.
Run Code Online (Sandbox Code Playgroud)

输出:

0
1
0
Run Code Online (Sandbox Code Playgroud)

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

......但是,我不开心.

LU *_* RD 11

来自documentation:

操作x shl y和x shr y将x的值向左或向右移位y位,其中(如果x是无符号整数)等于x乘以或除以2 ^ y; 结果与x的类型相同.例如,如果N存储值01101(十进制13),则N shl 1返回11010(十进制26).请注意,y的值以x的类型的大小为基础进行解释.因此,例如,如果x是整数,则x shl 40被解释为x sh18,因为整数是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;
Run Code Online (Sandbox Code Playgroud)

因此,当y是常量时,似乎存在64位值的编译器错误.

注意在64位模式下,1 shl 64导致1.

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

报道为QC112261 SHL operations by constant fails.


如果你想要的移位操作结果是0,因为ay值> = 64,那么可以使用这个函数:

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

更新

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

  • 这解释了故事的一半.但是为什么编译时在编译常量表达式时编译器不会使用相同的规则?在XE3中它是否仍然存在错误?32位和64位编译器? (2认同)
  • @DavidHeffernan,我是我公司的研发经理.我的工作涉及光学/机械结构以及用于气体和颗粒监测的建筑系统领域的软件工程.激光/干涉仪/光谱仪是我的日常工作.我们的软件中加入了令人难以置信的人工年数,用于数据收集/演示,以及GIS模型气象学和预测/临近预报. (2认同)