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亿次;所以速度很重要。
答案 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
已经Int64
,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);
答案 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;