关于左移和右移算子

时间:2014-06-06 11:06:19

标签: c bit-manipulation

void times(unsigned short int time)
{
    hours=time>>11;
    minutes=((time<<5)>>10);
}

将输入time设为24446

输出值

小时= 11

分钟= 763

预期值

小时= 11

分钟= 59

此代码中正在进行哪些内部处理?

24446的二进制文件为0101111101111110

Time>>11提供01011,表示11

((Time<<5)>>10)提供111011,表示59

但是这里还发生了什么?

3 个答案:

答案 0 :(得分:3)

这段代码似乎认为int是16位,左移位时间将清除前5位。

由于您最有可能使用32/64位,如果time中的值为16位值,则不会发生这种情况:

time >> 5 == (time << 5) >> 10

试试这个:

minutes = (time >> 5) & 0x3F;

minutes = (time & 0x07FF) >> 5;

time声明为unsigned short,并在每次移位操作后强制转换为unsigned short,因为数学是32/64位。

24446 in binary是:0101 1111 0111 1110

  • 比特0-4 - 未知
  • 比特5-10分钟
  • 比特11-16小时

答案 1 :(得分:2)

  

这里还有什么?

如果timeunsigned short,则

之间存在重要差异
minutes=((time<<5)>>10);

unsigned short temp = time << 5;
minutes = temp >> 10;

在两个表达式中,由于整数提升规则,time << 5被计算为int。 [注1和2]。

在第一个表达式中,此int结果右移10。在第二个表达式中,对unsigned short temp的赋值将结果缩小为short,然后右移10点。

因此,在第二个表达式中,高阶位被移除(通过强制转换为unsigned short),而在第一个表达式中,如果int宽于{{1},则不会删除它们}}

第一个表达还有另一个重要的警告。由于整数提升可能会将short更改为unsigned short,因此中间值可能会被签名,在这种情况下,如果左移足够大,则可能出现溢出。 (在这种情况下,它不是。)然后可以将右移应用于负数,结果是“实现定义的”;许多实现将负数的右移行为定义为符号扩展数。这也可能导致意外。


注意:

  1. 假设int宽于int。如果shortunsigned int的宽度相同,则不会进行转换,您也不会看到所描述的差异。 “整数促销”在C标准的§6.3.1.1/ 2中描述(使用C11草案):

      

    如果unsigned short可以表示原始类型的所有值(由宽度限制,对于a   位字段),该值转换为int;否则,它将转换为int。这些被称为整数促销。所有其他类型都没有改变   整数促销。

    整数提升规则有效地使得无法使用小于unsigned int的类型直接进行任何算术计算,尽管编译器可能会使用int规则来使用子字操作码。在这种情况下,它们必须产生与促进价值产生的结果相同的结果;这对于无符号加法和乘法很容易,但对于移位来说比较棘手。

  2. bitshift运算符是算术运算语义的一个例外。对于大多数算术运算,C标准要求在执行操作之前应用“通常的算术转换”。通常的算术转换,除其他外,保证两个操作数具有相同的类型,这也是结果的类型。对于位移,标准仅要求对两个操作数执行整数提升,并且结果将具有左操作数的类型。那是因为移位运算符不对称。对于几乎所有的体系结构,没有有效的右操作数用于不适合无符号字符的移位,并且显然不需要左右操作数的类型甚至签名都是相同的。

    无论如何,与所有算术运算符一样,将执行整数提升(至少)。因此,您不会看到比what-if更窄的中间结果。

答案 2 :(得分:0)

对于使用32位的平台,似乎'int'的大小。 就处理而言,假设, 第一个陈述是将“时间”除以“11”。 第二个陈述是将“时间”乘以5然后将整数除以10。 你的问题的答案在这里结束。

如果您添加实际包含的时间值(秒数/毫秒/小时或其他),那么您可能会获得更多帮助。

编辑: 正如@egur指出的那样,您可能正在将代码从16位移植到32/64位平台。 一种广泛接受的C编码风格使代码可移植如下:

制作Typedef.h文件并将其包含在每个其他C文件中,

 //Typedef.h

 typedef unsigned int   U16
 typedef signed int     S16
 typedef unsigned short U8
 typedef signed short   S8
    :
    :
 //END

在声明变量时使用U16,U8等。

现在当您移动到更大的位处理器(例如32位)时,请将Typedef.h更改为

 //Typedef.h

 typedef unsigned int   U32
 typedef signed int     S32
 typedef unsigned short U16
 typedef signed short   S16

无需在休息代码中更改任何内容。

EDIT2:

看到你的编辑后

  ((Time<<5)>>10) gives 111011 which means 59.

对于32位处理器

   ((Time<<5)>>10) gives 0000 0010 1111 1011 which means 763.