C编程语言的练习2-7:
写一个函数
invert(x,p,n)
,返回x
,其中n
位从位置p
开始反转(即1变为0,反之亦然),离开其他人不变。
我理解这样的问题:我有182个二进制101(101)10
,括号中的部分必须反转而不改变其余部分。返回值应该是10101010
,然后是十进制的170。
这是我的尝试:
#include <stdio.h>
unsigned int getbits(unsigned int bitfield, int pos, int num);
unsigned int invert(unsigned int bitfield, int pos, int num);
int main(void)
{
printf("%d\n", invert(182, 4, 3));
return 0;
}
/* getbits: get num bits from position pos */
unsigned int getbits(unsigned int bitfield, int pos, int num)
{
return (bitfield >> (pos+1-n)) & ~(~0 << num);
}
/* invert: flip pos-num bits in bitfield */
unsigned int invert(unsigned int bitfield, int pos, int num)
{
unsigned int mask;
unsigned int bits = getbits(bitfield,pos,num);
mask = (bits << (num-1)) | ((~bits << (pos+1)) >> num);
return bitfield ^ mask;
}
对我来说似乎是正确的,但invert(182, 4, 3)
输出536870730
。 getbits()
工作正常(直接来自本书)。我写下了我分配给y
的表达式中发生的事情:
(00000101 << 2) | ((~00000101 << 5) >> 3) -- 000000101 is the part being flipped: 101(101)10
00010100 | ((11111010 << 5) >> 3)
00010100 | (01000000 >> 3)
00010100 | 00001000
= 00011100
10110110 (182)
^ 00011100
----------
= 10101010 (170)
应该是正确的,但事实并非如此。我发现这是错误的地方:((~xpn << (p+1)) >> n)
。我不知道怎么做。
另外,我不知道这段代码有多普遍。我的首要任务是让这个案例有效。也欢迎这个问题的帮助。
答案 0 :(得分:4)
((1u<<n)-1)
是RHS的n
'1'位的位掩码。 <<p
将这个p
个位置的块移到左侧。 (如果你想从左边算起,你应该用(p-n)
而不是p来移动。
return val ^ (((1u<<n)-1) <<p) ;
当p大于单词大小时(仍然未定义单词大小的移位)仍然存在问题,但这应该是调用者的责任; - )
对于p = 2且n = 3的示例101(101)10
:
1u<<n := 1000
((1u<<n)-1) := 0111
(((1u<<n)-1) <<p) := 011100
original val := 10110110
val ^ mask := 10101010
答案 1 :(得分:1)
我认为你在其中一个转变中有一个一个一个问题(它只是一个预感,我并不完全确定)。然而,我保持简单(我假设索引位置p
从LSB开始,即p
= 0是 LSB ):
unsigned int getbits(unsigned int x, int p, int n) {
unsigned int ones = ~(unsigned int)0;
return x ^ (ones << p) ^ (ones << (p+n));
}
编辑:如果你需要p = 0作为 MSB ,只需反转移位(这可以正常工作,因为ones
定义为unsigned int
):
unsigned int getbits(unsigned int x, int p, int n) {
unsigned int ones = ~(unsigned int)0;
return x ^ (ones >> p) ^ (ones >> (p+n));
}
注意:在p < 0
,p >= sizeof(int)*8
,p+n < 0
或p+n >= sizeof(int)*8
这两种情况下,getbits
的结果都未定义。
答案 2 :(得分:0)
看一下Steve Summit的"Introductory C programming"和Ted Jensen的"At tutorial on pointers and arrays in C"。他们所涵盖的语言与今天的C语言略有不同(编程习俗已经发展,机器很多更大,真正的男人不再编写汇编程序了),但他们所说的很多内容都是真的今天就像那时一样。肖恩安德森的"Bit twiddling hacks"会使你的眼睛膨胀。保证。
答案 3 :(得分:0)
我发现我的实施中出了什么问题(除了从错误的方向计算num
之外)。之后看起来相当明显,我已经学到了更多关于比特的知识。
当1位向左移位时,超出位域范围,它会扩展。
1000 (8) << 1
== 10000 (16)
bitfield << n
将bitfield
乘以2 n
次。我的表达式((~bits << (pos+1)) >> num)
分别为bits
,pos
和num
的值为5,4和3。我将一个几乎与32位int大小相乘的数字乘以2,两次。
答案 4 :(得分:0)
我的功能怎么样?我认为这很好。
unsigned invert(unsigned x,int p,int n)
{
return (x^((~(~0<<n))<<p+1-n));
}