【问题标题】:promoting integer to long or unsigned long将整数提升为 long 或 unsigned long
【发布时间】:2015-05-14 21:04:20
【问题描述】:
static uint32_t get_num(void);

uint32_t get_count(unsigned int mask)
{
  uint8_t i = 0;
  uint32_t count = 0;

  while (i < get_num())
  {
     if (mask & (1 << i++))
       count++;
  }

  return count;
}

在这段代码中,(1L &lt;&lt; i++)(1UL &lt;&lt; i++) 哪个更安全?

【问题讨论】:

  • get_num的范围是多少?
  • 为什么要把i 变成uint8_t
  • 无。在表达式中使用副作用进行编程是邪恶的。
  • 请参阅Bit Twiddling Hacks 了解对整数中设置的位进行计数的更好方法。
  • @chux:最好避免使用有符号整数进行右移,因为无论移位是有符号还是无符号,标准都明确地保持开放。这被编译器合法利用,因为并非所有 CPU 都签署了右移指令。

标签: c types type-conversion


【解决方案1】:

无符号操作数更安全一些,因为只有这样才能在get_num() 返回该操作数类型的位数时定义所有移位的行为。如果unsigned longunsigned int 宽,那么ULU 稍微安全一些,但仅适用于容纳无论如何都无效的get_num() 结果。

然而,更安全的是:

uint32_t get_count(uint32_t mask)
{
  uint8_t num = get_num();

  if (num == 0) return 0;

  /* mask off the bits we don't want to count */
  mask &= ~((uint32_t) 0) >> ((num < 32) ? (32 - num) : 0);

  /* count the remaining 1 bits in mask, leaving the result in mask */
  mask = (mask & 0x55555555) + ((mask & 0xaaaaaaaa) >> 1);
  mask = (mask & 0x33333333) + ((mask & 0xcccccccc) >> 2);
  mask = (mask & 0x0f0f0f0f) + ((mask & 0xf0f0f0f0) >> 4);
  mask = (mask & 0x00ff00ff) + ((mask & 0xff00ff00) >> 8);
  mask = (mask & 0x0000ffff) + ((mask & 0xffff0000) >> 16);

  return mask;
}

【讨论】:

  • 在班次中使用完全匹配的尺寸类型:+1。优于UULL 等。
  • 恐怕缺少括号:+ 的绑定比 &gt;&gt; 更强
  • int-literals 应该是 UL(L 获得至少 32 位,U 因为这是无符号的)。此外,我会限制结果,而不是提前屏蔽:删除行 mask &amp;= ... 并更改 return mask &amp; ((1UL &lt;&lt; num) - 1); 确保条件和一些算术操作。 num 应该没有 stdtype (unsigned int) 或者是快速的类型之一(让 arch 选择最好的)。
  • unsigned long (UL 用于文字)至少是 32 位,所以是的,使用它是安全的!如果是 64 位,编译器可以(并将)将其优化为所有 32 位。
  • @Olaf,十六进制文字不需要 U 或 L。十六进制文字的值始终是无符号的,并且会自动为其选择有符号或无符号类型,该类型足够宽以容纳其值。 mask will be promoted, if necessary, for the bitwise &` 运算和求和,但这是保值的,结果可以安全地分配回 mask 而不会修改或丢失。
【解决方案2】:

如果您只想计算 uint 中的 1 位并使用 gcc,您应该查看内置函数(此处:int __builtin_popcount (unsigned int x))。这些可以预期得到高度优化,甚至在可用的情况下使用 CPU 的特殊指令。 (可以非常适合 gcc 测试)。

但是,不确定get_num() 会产生什么——它似乎不依赖于mask,所以它的输出可以用来限制popcount 的结果。

以下使用循环,并且在某些架构上可能比并行添加树更快(如果时间很重要,应该分析两个版本)。

unsigned popcount(uint32_t value, unsigned width)
{
    unsigned cnt = 0;   // actual size intentionally by arch

    if ( width < 32 )
        value &= (1UL << width) - 1;  // limit to actual width

    for ( ; value ; value >>= 1 ) {
        cnt += value & 1U;    // avoids a branch
    }
    return cnt;
}

注意宽度是传递给函数的。

在具有

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-29
    • 2016-04-19
    • 1970-01-01
    • 1970-01-01
    • 2014-05-03
    • 1970-01-01
    • 2016-12-20
    • 1970-01-01
    相关资源
    最近更新 更多