需要帮助理解“getbits()” K&RC第2章的方法

发布于 2024-07-07 22:15:48 字数 888 浏览 8 评论 0原文

在第 2 章有关按位运算符的部分(第 2.9 节)中,我无法理解示例方法之一的工作原理。

这里是提供的方法:

unsigned int getbits(unsigned int x, int p, int n) {
    return (x >> (p + 1 - n)) & ~(~0 << n);
}

这个想法是,对于给定的数字 x,它将返回从位置 p 开始的 n 位,从右边(最右边的位是位置 0)。 给定以下 main() 方法:

int main(void) {
    int x = 0xF994, p = 4, n = 3;
    int z = getbits(x, p, n);
    printf("getbits(%u (%x), %d, %d) = %u (%X)\n", x, x, p, n, z, z);

    return 0;
}

输出为:

getbits(63892 (f994), 4, 3) = 5 (5)

我得到了其中的一部分,但在“大局”方面遇到了麻烦,主要是因为这些位(没有双关语)我不明白。

我特别遇到问题的部分是补充部分:~(~0 << n)。 我想我明白了第一部分,处理x; 我正在努力解决的是这部分(然后是掩码)——以及如何将它们组合在一起以实际检索这些位。 (我已经验证了它正在做的事情,无论是使用代码还是使用 calc.exe 检查我的结果 - 感谢上帝,它有一个二进制视图!)

有什么帮助吗?

In chapter 2, the section on bitwise operators (section 2.9), I'm having trouble understanding how one of the sample methods works.

Here's the method provided:

unsigned int getbits(unsigned int x, int p, int n) {
    return (x >> (p + 1 - n)) & ~(~0 << n);
}

The idea is that, for the given number x, it will return the n bits starting at position p, counting from the right (with the farthest right bit being position 0). Given the following main() method:

int main(void) {
    int x = 0xF994, p = 4, n = 3;
    int z = getbits(x, p, n);
    printf("getbits(%u (%x), %d, %d) = %u (%X)\n", x, x, p, n, z, z);

    return 0;
}

The output is:

getbits(63892 (f994), 4, 3) = 5 (5)

I get portions of this, but am having trouble with the "big picture," mostly because of the bits (no pun intended) that I don't understand.

The part I'm specifically having issues with is the complements piece: ~(~0 << n). I think I get the first part, dealing with x; it's this part (and then the mask) that I'm struggling with -- and how it all comes together to actually retrieve those bits. (Which I've verified it is doing, both with code and checking my results using calc.exe -- thank God it has a binary view!)

Any help?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

哥,最终变帅啦 2024-07-14 22:15:49

我们使用 16 位作为示例。 在这种情况下,~0 等于

1111111111111111

当我们左移此 n 位(在您的情况下为 3)时,我们得到:

1111111111111000

因为 1左侧的 > 被丢弃,0 被输入到右侧。 然后重新补码得到:

0000000000000111

因此,这是在数字的最低有效部分中获取 n 1 位的巧妙方法。

您描述的“x 位”已将给定数字 (f994 = 1111 1001 1001 0100) 向右移动足够远,以便最低有效 3 位是您想要的。 在此示例中,您请求的输入位在那里,所有其他输入位都标记为 . 因为它们对最终结果并不重要:

ff94             ...........101..  # original number
>> p+1-n     [2] .............101  # shift desired bits to right
& ~(~0 << n) [7] 0000000000000101  # clear all the other (left) bits

如您所见,您现在拥有相关位,在最右边的位位置。

Let's use 16 bits for our example. In that case, ~0 is equal to

1111111111111111

When we left-shift this n bits (3 in your case), we get:

1111111111111000

because the 1s at the left are discarded and 0s are fed in at the right. Then re-complementing it gives:

0000000000000111

so it's just a clever way to get n 1-bits in the least significant part of the number.

The "x bit" you describe has shifted the given number (f994 = 1111 1001 1001 0100) right far enough so that the least significant 3 bits are the ones you want. In this example, the input bits you're requesting are there, all other input bits are marked . since they're not important to the final result:

ff94             ...........101..  # original number
>> p+1-n     [2] .............101  # shift desired bits to right
& ~(~0 << n) [7] 0000000000000101  # clear all the other (left) bits

As you can see, you now have the relevant bits, in the rightmost bit positions.

2024-07-14 22:15:49

我想说最好的办法就是亲手解决问题,这样你就会明白它是如何工作的。

这是我使用 8 位无符号整数所做的事情。

  1. 我们的数字是 75,我们需要从位置 6 开始的 4 位。
    该函数的调用将是 getbits(75,6,4);

  2. 二进制中的 75 是 0100 1011

  3. 因此,我们创建一个 4 位长的掩码,从最低位开始,这样做是这样的。

~0 = 1111 1111
<<4 = 1111 0000
~ = 0000 1111

好的,我们拿到了面具。

  1. 现在,我们将所需的位数从数字中推入最低位,这样
    我们将二进制 75 移位 6+1-4=3。

0100 1011 >>3 0000 1001

现在我们有了低位中正确位数的掩码以及我们想要的低位原始数字中的位。

  1. 所以我们& 它们
  0000 1001 
& 0000 1111 ============ 0000 1001

所以答案是十进制9。

注意:高阶半字节恰好全为零,在这种情况下使得掩码多余,但它可能是任何值,具体取决于我们开始的号码。

I would say the best thing to do is to do a problem out by hand, that way you'll understand how it works.

Here is what I did using an 8-bit unsigned int.

  1. Our number is 75 we want the 4 bits starting from position 6.
    the call for the function would be getbits(75,6,4);

  2. 75 in binary is 0100 1011

  3. So we create a mask that is 4 bits long starting with the lowest order bit this is done as such.

~0 = 1111 1111
<<4 = 1111 0000
~ = 0000 1111

Okay we got our mask.

  1. Now, we push the bits we want out of the number into the lowest order bits so
    we shift binary 75 by 6+1-4=3.

0100 1011 >>3 0000 1001

Now we have a mask of the correct number of bits in the low order and the bits we want out of the original number in the low order.

  1. so we & them
  0000 1001 
& 0000 1111 ============ 0000 1001

so the answer is decimal 9.

Note: the higher order nibble just happens to be all zeros, making the masking redundant in this case but it could have been anything depending on the value of the number we started with.

没︽人懂的悲伤 2024-07-14 22:15:49

~(~0 << n) 创建一个掩码,该掩码将打开 n 最右边的位。

0
   0000000000000000
~0
   1111111111111111
~0 << 4
   1111111111110000
~(~0 << 4)
   0000000000001111

将结果与其他内容进行 AND 运算将返回那些 n 位中的内容。

编辑:我想指出我一直在使用的这个程序员计算器: AnalogX PCalc

~(~0 << n) creates a mask that will have the n right-most bits turned on.

0
   0000000000000000
~0
   1111111111111111
~0 << 4
   1111111111110000
~(~0 << 4)
   0000000000001111

ANDing the result with something else will return what's in those n bits.

Edit: I wanted to point out this programmer's calculator I've been using forever: AnalogX PCalc.

反话 2024-07-14 22:15:49

还没有人提到它,但在 ANSI C ~0 << n 导致未定义的行为。

这是因为 ~0 是负数,而左移负数未定义。

参考:C11 6.5.7/4(早期版本有类似文本)

E1 << 的结果 E2E1 左移 E2 位位置; 空出的位用零填充。 [...] 如果 E1 有签名
类型和非负值,并且 E1 × 2E2 可以在结果类型中表示,那么这就是结果价值; 否则,行为未定义。

在 K&RC 中,此代码将依赖于 K&R 开发的特定系统类,在执行有符号数的左移时,天真地将 1 位从左侧移出(并且此代码也依赖于 2 的补码表示),但其他一些系统不共享这些属性,因此 C 标准化过程没有定义此行为。

因此,这个示例实际上只是作为历史好奇心而有趣,自 1989 年以来(如果不是更早),它不应该在任何实际代码中使用。

Nobody mentioned it yet, but in ANSI C ~0 << n causes undefined behaviour.

This is because ~0 is a negative number and left-shifting negative numbers is undefined.

Reference: C11 6.5.7/4 (earlier versions had similar text)

The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. [...] If E1 has a signed
type and nonnegative value, and E1 × 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

In K&R C this code would have relied on the particular class of system that K&R developed on, naively shifting 1 bits off the left when performing left-shift of a signed number (and this code also relies on 2's complement representation), but some other systems don't share those properties so the C standardization process did not define this behaviour.

So this example is really only interesting as a historical curiosity, it should not be used in any real code since 1989 (if not earlier).

最美不过初阳 2024-07-14 22:15:49

使用示例:
int x = 0xF994,p = 4,n = 3;
int z = getbits(x, p, n);

并重点关注这组操作
~(~0 << n)

对于任何位集(10010011 等),您想要生成一个“掩码”,仅提取您想要查看的位。 所以 10010011 或 0x03,我对 xxxxx011 感兴趣。 提取该集合的掩码是什么? 00000111 现在我想要 sizeof int 独立,我将让机器完成工作,即从 0 开始,对于字节机器,它是 0x00 对于字机器,它是 0x0000 等等。64 位机器将由 64 位或 0x0000000000000000 表示

现在应用“not”(~0)并得到 11111111
右移 (<<) n 并得到 11111000
并“不是”那个并得到 00000111

所以 10010011 & 00000111 = 00000011

你还记得布尔运算是如何工作的吗?

Using the example:
int x = 0xF994, p = 4, n = 3;
int z = getbits(x, p, n);

and focusing on this set of operations
~(~0 << n)

for any bit set (10010011 etc) you want to generate a "mask" that pulls only the bits you want to see. So 10010011 or 0x03, I'm interested in xxxxx011. What is the mask that will extract that set ? 00000111 Now I want to be sizeof int independent, I'll let the machine do the work i.e. start with 0 for a byte machine it's 0x00 for a word machine it's 0x0000 etc. 64 bit machine would represent by 64 bits or 0x0000000000000000

Now apply "not" (~0) and get 11111111
shift right (<<) by n and get 11111000
and "not" that and get 00000111

so 10010011 & 00000111 = 00000011

You remember how boolean operations work ?

一枫情书 2024-07-14 22:15:49

在 ANSI C ~0 >> 中 n 导致未定义的行为

// 关于左移导致问题的帖子是错误的。

无符号字符 m,l;

m = ~0>> 4; 产生 255,它等于 ~0,但是

m = ~0;
l = m>> 4; 产生正确的值 15 与:

m = 255 >>> 4;

左移负数 ~0 << 没有任何问题

In ANSI C ~0 >> n causes undefined behavior

// the post about left shifting causing a problem is wrong.

unsigned char m,l;

m = ~0 >> 4; is producing 255 and its equal to ~0 but,

m = ~0;
l = m >> 4; is producing correct value 15 same as:

m = 255 >> 4;

there is no problem with left shifting negative ~0 << whatsoever

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文