【问题标题】:What is the most portable way to read and write the highest bit of an integer in C?在 C 中读取和写入整数的最高位的最便携的方法是什么?
【发布时间】:2011-06-15 15:09:24
【问题描述】:

在 C 中读取和写入整数的最高位最便携的方法是什么?

这是一个彭博采访问题。当时我没有给出最佳答案。谁能解答一下?

【问题讨论】:

  • GNU C 不是很便携...

标签: c bitwise-operators


【解决方案1】:
#define HIGH_BIT(inttype) (((inttype)1) << (CHAR_BIT * sizeof(inttype) - 1))

示例用法:

ptrdiff_t i = 4711;
i |=  HIGH_BIT(ptrdiff_t);  /* set high bit */
i &= ~HIGH_BIT(ptrdiff_t);  /* clear high bit */

【讨论】:

  • 请添加一些解释。
【解决方案2】:

首先,请注意,如果我们谈论有符号整数,则没有可移植的方式来访问最高位;标准中根本没有定义单一的可移植表示,因此“top bit”的含义原则上可以有所不同。此外,C 不允许直接访问按位表示;您可以将 int 作为 char 缓冲区访问,但您不知道“最高位”的位置。

如果我们只关心有符号整数的非负范围,并假设该范围的大小是 2 的幂(如果不是,那么我们需要再次关心有符号表示):

#define INT_MAX_BIT (INT_MAX - (INT_MAX >> 1))
#define SET_MAX_BIT(x) (x | INT_MAX_BIT)
#define CLEAR_MAX_BIT(x) (x & ~INT_MAX_BIT)

类似的方法可以用于无符号整数,它可以用来获取真正的最高位。

【讨论】:

  • 这仅适用于在limits.h 中指定限制的整数类型。例如,它不适用于off_t
  • @R,是的,但很难看到一种不使用 _MAX 宏且不调用未定义(或实现定义)行为的高效、可移植方法...
  • 对不起,你能解释一下为什么“#define INT_MAX_BIT (INT_MAX - (INT_MAX >> 1))”有效吗?没看懂
  • 通常INT_MAX 的值设置为全 1(假设对于某些 n,INT_MAX 为 2^n - 1)。右移一位会导致最高位被清除。然后从原始中减去只剩下最高位。
  • 该标准实际上对整数类型的表示施加了相当严格的限制——例如,有符号和无符号类型的最大值都必须是2**N - 1。请参阅第 6.2.6.2 节。
【解决方案3】:

这是一个愚蠢的,使用:

Built-in Function: int __builtin_clz (unsigned int x)

Returns the number of leading 0-bits in x, starting at the most
significant bit position. If x is 0, the result is undefined. 

第一次尝试:

int get_msb(int x) { return x ? __buildin_clz(x) == 0 : 0; }

注意:C 的一个怪癖是指定intunsigned int 参数的函数可以用其他类型调用而不会发出警告。但是,这可能涉及转换 - C++ 标准 4.7.2 说:

如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模 2n,其中 n 是用于表示无符号类型的位数)。 [注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。 ]

这意味着如果位模式不是二进制补码表示,则可能会更改位模式,这也会阻止此“解决方案”可靠地工作。 :-(

Chris 在下面的评论提供了一个解决方案(在此合并为一个函数而不是预处理器宏):

int get_msb(int x) { return x ? __buildin_clz(*(unsigned*)&x) == 0 : 0; }

【讨论】:

  • 解决方法是#define msb(x) __builtin_clz(*(unsigned)&amp;x),但是你不能在文字数字上使用它。 GCC 解决方法是 #define msb(x) ({ typeof(x) _x = x; __builtin_clz(*(unsigned)&amp;_x); })
  • @Chris:这很好......在int x 参数上方的函数中无论如何都会收到文字,然后您可以按照您的建议进行转换。我将更新上面的代码。谢谢!
  • 强制转换在技术上是未定义的行为,这意味着它不能保证有效。在实践中,它会将有符号值的地址重新解释为无符号值(这是正确的未定义行为,因为标准没有指定特定的符号表示),就像union { int i; unsigned u; } u; u.i = x; return __builtin_clz(u.j); 一样。我使用了一个宏,以便它可以在签名和未签名的int 类型上工作,只为签名版本调用 UB。但无论您采用何种方式,最终都必然依赖于平台。
  • @Chris:是的,我知道,但如果 GCC 本身不能在任何古怪的系统上运行,我不会感到惊讶...... :-)。
【解决方案4】:

这个有什么问题?

int get_msb(int n){
    return ((unsigned)n) >> (sizeof(unsigned) * CHAR_BIT - 1);
    // or, optionally
    return n < 0;
};

int set_msb(int n, int msb){
    if (msb)
         return ((unsigned)n) |  (1ULL << (sizeof(unsigned) * CHAR_BIT - 1));
    else return ((unsigned)n) & ~(1ULL << (sizeof(unsigned) * CHAR_BIT - 1));
};

它负责字节序、字节中的位数,并且也适用于 1 的补码。

【讨论】:

  • 这假定二进制补码表示。 OP要求一种便携的方式:)
  • C 标准不将选项限制为“二或一补”。根据 6.5.7.5,负值右移的结果完全由实现定义;超出正值范围的左移结果是未定义的,原则上甚至可能崩溃。
  • 同样不可移植,因为sizeof(X)*CHAR_BIT 假定没有填充位。
  • 通过使用 [modular] 算术而不是 bit hacks。
【解决方案5】:

如果类型是无符号的,那就简单了:

(type)-1-(type)-1/2

对于带符号的值,我不知道。如果您找到一种方法,它将回答关于 SO 的几个未回答的问题:

C question: off_t (and other signed integer types) minimum and maximum values

Is there any way to compute the width of an integer type at compile-time?

也许是其他人。

【讨论】:

  • 你是什么意思(类型)-1?你能给我真正的C代码吗?使困惑 。也许我没有明白。对不起
  • (type) 是类型转换为type。例如,您可以使用(unsigned int)-1 - (unsigned int) -1 / 2
  • 你得到了什么结果,你期望什么?在具有 32 位 int 的系统上,(unsigned)-1-(unsigned)-1/2 按预期给了我0x80000000
  • 另一种可能性是~((unsigned)-1 &gt;&gt; 1)
  • 有趣的是,答案的可读性比空格 ala (type)-1 - (type)-1 / 2... :-/.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-18
  • 1970-01-01
  • 2016-07-11
相关资源
最近更新 更多