我不能使用带有int64变量的SHL(向左移位)

时间:2011-05-16 09:37:00

标签: delphi

 Result:= b8;
 Result:= Result+ (b7  SHL  8);
 Result:= Result+ (b6  SHL 16);
 Result:= Result+ (b5  SHL 24);
 Result:= Result+ (b4  SHL 32);
 Result:= Result+ (b3  SHL 40);      <------ here
 Result:= Result+ (b2  SHL 48);
 Result:= Result+ (MSB SHL 56);

(结果为int64,b1-b8为字节)。

但编译器抱怨“常量exp违反了子范围”。 然后我发现了这个警告“编译器将拒绝超过32的硬编码移位权限值,除非数据类型是Int64。对于网站上的Shl”也是如此。 Delphi Help对此一无所知。为什么有限制?我可以阻止它吗?也许是编译指令还是什么?我可以使用乘法来克服它,但速度会慢一些。有更快的方法吗?

编辑: 我也想创建两个红衣主教,因为我可以在红衣主教上使用SHL,然后将红衣主教合并在一起。这只需要一次乘法。还有其他想法吗?

EDIT2: 该代码是算法的一部分,该算法将数字形式基数255转换为基数256(反之亦然)。我这样做了5亿次;所以速度很重要。

4 个答案:

答案 0 :(得分:17)

我会避免所有算术(你的代码有添加和移位),并按照这样做:

Int64Rec(Result).Bytes[0] := b8;
Int64Rec(Result).Bytes[1] := b7;
//etc.

Int64Rec在SysUtils中定义如下:

Int64Rec = packed record
  case Integer of
    0: (Lo, Hi: Cardinal);
    1: (Cardinals: array [0..1] of Cardinal);
    2: (Words: array [0..3] of Word);
    3: (Bytes: array [0..7] of Byte);
end;

如果您将字节存储在数组中,那么可以将它全部包装在for循环中。

答案 1 :(得分:11)

我假设您的Result已经Int64b8b7等等被声明为Byte。编译器需要一些帮助。 Typecast到Int64

  Result := b8;
  Result := Result + (b7 shl  8);
  Result := Result + (b6 shl 16);
  Result := Result + (b5 shl 24);
  Result := Result + (Int64(b4) shl 32);
  Result := Result + (Int64(b3) shl 40);
  Result := Result + (Int64(b2) shl 48);
  Result := Result + (Int64(msb) shl 56);

答案 2 :(得分:8)

这是另一种使用轮班的解决方案,但我相信它“更干净”(虽然不像大卫的建议那么干净):

result := MSB;
result := (result shl 8) or b2;  { could use "shl sizeof(b2)" instead of 8 }
result := (result shl 8) or b3;
etc
result := (result shl 8) or b8;

这个解决方案避免了所有“神奇”的移位值,它可能更容易理解。此外,如果MSB..b8(或b1..b8)是一个字节数组,上面的代码可以很容易地转换成单行循环。

要回答关于为什么编译器不接受40及以上值的问题,原因很可能是由于SHLD指令的英特尔指令集参考卷2B的引用:

  

在非64位模式下,默认为64位   模式;只有0到4位   使用计数。这掩盖了计数   到0到31之间的值。如果a   count大于操作数   大小,结果未定义。

SHL指令的等效条件不是那么严格:

  

8086并没有掩盖这种转变   计数。但是,所有其他IA-32   处理器(从英特尔开始)   286处理器)屏蔽移位计数   到5位,产生最大值   这个掩蔽完成了   所有操作模式(包括   virtual-8086模式)减少   最大执行时间   指令。

在任何一种情况下,大于31的值无用(使用SHL时)或未定义(使用SHLD时)。编译器显然知道这一点,并且它阻止您编写可能错误的代码(在64位模式下。)

如果您确实正在进行50亿次此操作,那么您可能需要考虑在内联汇编中进行,这很简单:

asm
    xor eax, eax   
    or  eax, MSB   { or b1 depending on how you named the MSB }
    shl eax, 8
    or  eax, b2
    shl eax, 8
    or  eax, b3
    shl eax, 8
    or  eax, b4
    mov High32bit, eax
end;

并对低32位双字和b5至b8重复上述操作。我没有建议使用SHLD,因为我不相信Delphi支持64位寄存器或64位指令(我可能错了,我从未尝试过。)

注意:我听说有传言称64位版本的Delphi不支持内联汇编。这可能是也可能不是这样,但我会远离内联汇编,除非它确实是绝对必要的。

希望有所帮助,

约翰。

PS:大卫·赫弗南的解决方案是最好的解决方案还有另外一个原因。在我提出的解决方案中,每条指令都依赖于前一条指令(即,在执行下一条“或”指令之前,eax必须移位8)。 David的解决方案设置单个字节,因此,每个分配独立于先前的分配,这将允许处理器并行地执行多个分配。在这个多核处理器的时代,这可能比我给出的汇编代码快一点。

答案 3 :(得分:0)

这里有一个将Integer转换为int64bit的函数: 例如:AData = 36->将设置位36,等等

uses SysUtils;

[...]

function IntegertoInt64Bit(AData : Integer): UInt64;
  var
    idx : integer;
  begin
    result := 0;
    for idx := 7 downto 0 do
    begin
      if(AData >= idx * 8) then
      begin
        Int64Rec(result).Bytes[idx] := 1 shl ((AData-1) mod 8);
        break;
      end;
    end;
  end;