【问题标题】:I cannot use SHL (shift left) with int64 variables我不能将 SHL(左移)与 int64 变量一起使用
【发布时间】:2011-05-16 09:36:59
【问题描述】:
 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,然后将红衣主教合并在一起。这将只需要一次乘法。还有其他想法吗?

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

【问题讨论】:

  • 结果是 Int64 类型?还有b3?你能插入一个类型转换到 Int64: Int64(b3) 吗?
  • 对不起。对不起。我完全忘记了这一点。我更新了我的问题。

标签: delphi


【解决方案1】:

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

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 循环中。

【讨论】:

  • @Altar 随便。我不同意您必须回答所提出的问题的想法。根据我的开发经验,最难的部分是找出正确的问题。所以我总是尝试考虑任何问题的替代方法。我在做日常工作时就是这样做的,我认为没有理由改变策略。
  • @Altar 无需创建新问题,我接受了您接受另一个答案的推理。你没有冒犯我。大多数情况下,我感到难过的是,不雅的代码在它旁边打勾,这可能会鼓励其他开发人员继续使用移位和魔术常量来解决此类问题。如果它实际上详细说明编译器为何如此行事(并且这样做有很好的理由)并解释说有更优雅的方法可以实现这一特定目标,那么接受的答案会好得多。
  • +1 为了优雅,shl 有它的用途,但这不是其中之一,你的代码要优越得多。
  • 不错的一个.. 易于阅读,而且应该很快.. 如果您的代码需要可移植,我想您需要在这里保持字节序,对吧?
  • @wouter 是的,这是真的。我从来没有为大端机器编码过。我没有处理它的常用技术的经验。
【解决方案2】:

我假设您的Result 已经是Int64,并且b8b7 等被声明为Byte。编译器需要一点帮助。类型转换为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);

【讨论】:

  • 我仍然对这种溶液有强烈的过敏反应。我同意它可以工作,但为什么要使用所有这些算术、位移、魔术常数?当然最好直接分配每个字节?当存在优雅的选项时,我不能认可这样的代码。
  • 是的,但问题是专门针对 shl 的。
  • @David :这绝对是对所提出问题的正确答案。我同意可能应该问一个不同的问题,但也许代码 sn-p 只是显示 SHL 问题的糟糕选择?
  • @TOndrej 但 OP 甚至表达了对性能的担忧。很难想象内存拷贝 + int64 算法能比普通内存拷贝更快。
  • 对性能的关注是没有意义的,因为我们不知道上下文,甚至不知道预期的目标。我刚刚回答了关于 shl 的具体问题,因为我今天不想玩 Columbo。 ;-)
【解决方案3】:

这是另一种使用轮班的解决方案,但我认为它“更干净”(虽然不如 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 之间的值。如果 计数大于操作数 大小,结果未定义。

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, 8
    or  eax, b3
    shl eax, 8
    or  eax, b4
    mov High32bit, eax
end;

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

注意:我听说 64 位版本的 Delphi 将不支持内联汇编。情况可能会,也可能不会,但除非真的绝对必要,否则我会远离内联汇编。

希望对你有帮助,

约翰。

PS:David Heffernan 的解决方案是最佳解决方案还有另一个原因。在我提出的解决方案中,每条指令都依赖于前一条指令(也就是说,在执行下一条“或”指令之前,eax 必须移动 8 位)。 David 的解决方案设置了单独的字节,因此,每个分配都独立于先前的分配,这将允许处理器潜在地并行执行多个分配。在这个多核处理器时代,这有可能比我作为示例给出的汇编代码快很多。

【讨论】:

    【解决方案4】:

    这是一个将 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;
    

    【讨论】:

      猜你喜欢
      • 2020-06-19
      • 1970-01-01
      • 2020-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多