问题描述
|
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违反了子范围边界”。
然后,我在网站上发现以下警告:“除非数据类型为Int64,否则编译器将拒绝超过32的硬编码移位右值。对于Shl \而言,情况并非如此”。 Delphi Help对此一无所获。为什么有限制?我可以阻止它吗?也许是编译器指令之类的?我可以使用乘法来克服它,但是会慢一些。有更快的方法吗?
编辑:
我也在考虑创建两个基数,因为我可以在基数上使用SHL,然后将基数合并在一起。这将只需要一个乘法。还有其他想法吗?
编辑2:
该代码是算法的一部分,该算法将数字从255转换为256(以相反的方式)。我做了500亿次;所以速度很重要。
解决方法
我会避免所有算术运算(您的代码具有加法运算和移位运算),并这样做:
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;
如果将字节存储在数组中,则可以将其全部包裹为“ 4”循环。
,我假设您的Result
已经是Int64
,并且b8
,b7
等被声明为Byte
。编译器需要一点帮助。键入to6ѭ:
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);
,这是使用班次的另一种解决方案,但我相信它是“更干净的”(尽管不如David的建议那么干净):
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
计数大于操作数
大小,结果不确定。
SHL指令上的等效条件并不那么严格:
8086不会掩盖这一转变
计数。但是,所有其他IA-32
处理器(从英特尔开始
286处理器)掩盖移位计数
到5位,导致最大值
计数为31。此掩蔽是在
所有操作模式(包括
virtual-8086模式)以减少
的最大执行时间
说明。
在这两种情况下,大于31的值都是无用的(使用SHL时)或未定义的(使用SHLD时)。编译器显然知道这一点,并且它阻止了您编写潜在错误的代码(在64位模式下)。
如果您确实要执行此操作500亿次,那么您可能要考虑以内联汇编的方式进行操作,这将非常简单:
asm
xor eax,eax
or eax,MSB { or b1 depending on how you named the MSB }
shl eax,8
or eax,b2
shl eax,b3
shl eax,b4
mov High32bit,eax
end;
对低32位dword和b5至b8重复上述操作。我不建议使用SHLD,因为我不相信Delphi支持64位寄存器或64位指令(我可能是错的,我从未尝试过。)
注意:我听说有传言称64位版本的Delphi将不支持内联汇编。可能会,也可能不是,但除非真正绝对必要,否则我将避免进行内联汇编。
希望能有所帮助,
约翰。
PS:还有另一个原因使David Heffernan的解决方案是最好的解决方案。在我介绍的解决方案中,每条指令都取决于上一条指令(也就是说,必须在执行下一条“或”指令之前将eax移位8)。 David的解决方案设置单个字节,因此,每个分配都独立于先前的分配,这将允许处理器潜在地并行执行多个分配。在这个多核处理器时代,它的潜力可能比我作为示例给出的汇编代码快很多。