为什么移位32位值时,只使用移位操作数的低5位? (例如(UInt32的)1所述;γ-33 == 2) [英] Why use only the lower five bits of the shift operand when shifting a 32-bit value? (e.g. (UInt32)1 << 33 == 2)

查看:859
本文介绍了为什么移位32位值时,只使用移位操作数的低5位? (例如(UInt32的)1所述;γ-33 == 2)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑下面的代码:

UInt32 val = 1;
UInt32 shift31 = val << 31;                    // shift31  == 0x80000000
UInt32 shift32 = val << 32;                    // shift32  == 0x00000001
UInt32 shift33 = val << 33;                    // shift33  == 0x00000002
UInt32 shift33a = (UInt32)((UInt64)val << 33); // shift33a == 0x00000000



它不产生警告(约使用移位大于32 ),所以它必须是一个预期行为。

It doesn't generate a warning (about using a shift greater than 32) so it must be an expected behavior.

这居然被拿出来生成的程序集(或至少反射的代码的解释)的代码是

The code that actually gets put out to the generated assembly (or at least Reflector's interpretation of the code) is

 uint val = 1;
 uint shift31 = val << 0x1f;
 uint shift32 = val;
 uint shift33 = val << 1;
 uint shift33a = val << 0x21;



在IL(再次,使用反射)是

The IL (again, using Reflector) is

L_0000: nop 
L_0001: ldc.i4.1 
L_0002: stloc.0 
L_0003: ldloc.0 
L_0004: ldc.i4.s 0x1f
L_0006: shl 
L_0007: stloc.1 
L_0008: ldloc.0 
L_0009: stloc.2 
L_000a: ldloc.0 
L_000b: ldc.i4.1 
L_000c: shl 
L_000d: stloc.3 
L_000e: ldloc.0 
L_000f: conv.u8 
L_0010: ldc.i4.s 0x21
L_0012: shl 
L_0013: conv.u4 
L_0014: stloc.s shift33a

我理解的什么是怎么回事(它在的 MSDN );当代码编译,转移一个32位的值时,只使用低5位......我很好奇,为为什么发生这种情况。

I understand what is going on (it's described in MSDN); when the code is compiled, only the lower 5 bits are being used when shifting a 32-bit value... I'm curious as to why this happens.

(方式 shift33a 出来也让我觉得自己是不是很正确与反射,因为他们白细胞介素的C#介绍将编译成不同的东西)

(The way shift33a comes out also makes me think that something isn't quite right with Reflector, as their c# presentation of the IL will compile to something different)

的问题(S):


  • 为什么只能通过转移价值的低5位使用吗?

  • 如果它没有任何意义,以超过31位移位,为什么没有一个警告?

  • 这是一个倒退compatilbility的事情(即请问这是什么程序员预期的情况发生)?

  • 我是正确的,因为根本IL可以在做的超过31位的变化(如L_0010:ldc.i4.s为0x21 ),但编译器是修剪值

  • Why are only the lower 5 bits of "the value to shift by" used?
  • If "it doesn't make sense to shift more than 31 bits", why isn't there a warning?
  • Is this a backwards compatilbility thing (i.e. is this what programmers "expect" to happen)?
  • Am I correct that the underlying IL can do shifts of more than 31 bits (as in L_0010: ldc.i4.s 0x21) but the compiler is trimming the values?

推荐答案

这基本上可以归结为x86的处理算法转换操作码的方式:它仅使用移位计数的底部5位。请参阅 80386编程指南,例如。在C / C ++,它在技术上未定义的行为由超过31位(32位整数)做了移位,以你不付你不需要的东西的C哲学去。从部分6.5.7,C99标准的第3段:

It basically boils down to the way the x86 handles the arithmetic shift opcodes: it only uses the bottom 5 bits of the shift count. See the 80386 programming guide, for example. In C/C++, it's technically undefined behavior to do a bit shift by more than 31 bits (for a 32-bit integer), going with the C philosophy of "you don't pay for what you don't need". From section 6.5.7, paragraph 3 of the C99 standard:

整数促销活动在每个操作数执行。该结果的类型是,促进左操作数。如果右操作数的值是负数或
大于或等于促进左操作数的宽度,其行为是不确定的。

The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

这使编译器可省去在x86上的转变单一的移位指令。 64位移位不能在x86上的一个指令来完成。他们使用 SHLD /的SHRD说明加上一些额外的逻辑。在x86_64,64位移位可以在一个指令来完成。

This allows compilers to omit a single shift instruction on x86 for shifts. 64-bit shifts cannot be done in one instruction on x86. They use the SHLD/SHRD instructions plus some additional logic. On x86_64, 64-bit shifts can be done in one instruction.

例如,GCC 3.4.4发出以下大会由64位左移任意金额(以 -O3 -fomit-frame-pointer的编译):

For example, gcc 3.4.4 emits the following assembly for a 64-bit left-shift by an arbitrary amount (compiled with -O3 -fomit-frame-pointer):

uint64_t lshift(uint64_t x, int r)
{
  return x << r;
}

_lshift:
    movl    12(%esp), %ecx
    movl    4(%esp), %eax
    movl    8(%esp), %edx
    shldl   %cl,%eax, %edx
    sall    %cl, %eax
    testb   $32, %cl
    je      L5
    movl    %eax, %edx
    xorl    %eax, %eax
L5:
    ret

现在,我不是很熟悉C#,但我猜它有一个类似的理念 - 设计语言,以使其能够尽可能有效实施成为可能。通过指定移位操作只使用移位计数的底部5/6位,它允许JIT编译器编译变化尽可能优化。 32位的变化,以及在64位的系统的64位的变化,可以得到的JIT编译成单个操作码。

Now, I'm not very familiar with C#, but I'm guessing it has a similar philosophy -- design the language to allow it to be implemented as efficiently as possible. By specifying that shift operations only use the bottom 5/6 bits of the shift count, it permits the JIT compiler to compile the shifts as optimally as possible. 32-bit shifts, as well as 64-bit shifts on 64-bit systems, can get JIT compiled into a single opcode.

如果C#被移植到一个平台,该平台有它的原生操作码转移不同的行为,那么这实际上将产生额外性能命中 - JIT编译器必须确保标准得到尊重,所以必须添加额外的逻辑,以确保只有底部5/6用于移位计数位。

If C# were ported to a platform that had different behavior for its native shift opcodes, then this would actually incur an extra performance hit -- the JIT compiler would have to ensure that the standard is respected, so it would have to add extra logic to make sure only the bottom 5/6 bits of the shift count were used.

这篇关于为什么移位32位值时,只使用移位操作数的低5位? (例如(UInt32的)1所述;γ-33 == 2)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆