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

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

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

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

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

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

Dav*_*nan 17

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

Int64Rec(Result).Bytes[0] := b8;
Int64Rec(Result).Bytes[1] := b7;
//etc.
Run Code Online (Sandbox Code Playgroud)

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

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

  • @Altar无论如何.我不认为你必须回答被问到的问题.根据我的发展经验,最困难的部分是找出正确的问题.所以我总是试着考虑任何问题的替代方法.这就是我在做日常工作时的表现,我认为没有理由改变对策. (8认同)
  • @Altar没有必要创建一个新问题,我接受了你接受另一个答案的理由.你没有冒犯我.大多数时候,我感到很遗憾的是,不合适的代码会在它旁边打勾,这可能会鼓励其他开发人员继续使用移位和魔术常数来解决这些问题.如果它实际上已经详细说明了编译器为什么会像它一样运行(并且它出于非常好的理由这样做)并且解释说有更优雅的方法来实现这个特定目标,那么接受的答案会好得多. (3认同)
  • 不错.. 易于阅读,而且应该很快.. 如果您的代码需要可移植,我想您需要在这里保持字节序,对吗? (2认同)

Ond*_*lle 11

我假设你ResultInt64已经,并且b8,b7等被声明为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);
Run Code Online (Sandbox Code Playgroud)

  • 是的,但问题是关于shl具体. (3认同)
  • @David:这绝对是提出问题的正确答案.我同意应该提出一个不同的问题,但是代码片段可能只是显示SHL问题的不好选择? (2认同)

Hex*_*0bx 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;
Run Code Online (Sandbox Code Playgroud)

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

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

在非64位模式和默认的64位模式下; 仅使用计数的第0位到第4位.这会将计数屏蔽为0到31之间的值.如果计数大于操作数大小,则结果是未定义的.

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

8086不掩盖班次计数.但是,所有其他IA-32处理器(从Intel 286处理器开始)确实将移位计数屏蔽为5位,最大计数为31.此屏蔽在所有操作模式(包括虚拟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;
Run Code Online (Sandbox Code Playgroud)

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

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

希望有所帮助,

约翰.

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