如何从32位数字中提取位?

3

我对C语言的知识不是很了解,由于我的同事休假,我遇到了一个问题。

我有一个32位数字,需要从中提取一些位。我已经阅读了一些讨论帖,但仍然不清楚如何操作。如果有人能帮助我,我将非常感激。

以下是我需要完成的示例:

假设十六进制数= 0xD7448EAB
二进制为= 1101 0111 0100 0100 1000 1110 1010 1011.
我需要提取16位,并输出该值。我想要的是第10到25位。

忽略较低的10位(十进制),即10 1010 1011被忽略。
忽略上面的6位(溢出),即1101 01被忽略。

剩下的16位数据需要作为输出,即11 0100 0100 1000 11(斜体数字是所需的输出)。

这只是一个示例,我将一直得到不同的十六进制数字,并且需要提取与我解释的相同的位。

我该如何解决?
谢谢。

对于此示例,您将输出1101 0001 0010 0011,即0xD123或十进制的53539。


你需要一直保持二进制形式吗?或者你能够将十六进制值转换为二进制,然后将二进制值转换为字符串。然后你只需取出你感兴趣的子字符串,并根据需要将其转换回二进制/十六进制即可。 - dub stylee
1
使用“&”运算符将您的值与一个设置了您所感兴趣的位的掩码进行“与”操作,然后向右移动结果(“>>”运算符)以将它们与第零位对齐。 - 500 - Internal Server Error
4个回答

3

好的,这是我写的方式:

#include <stdint.h>
#include <stdio.h>

main() {
    uint32_t in = 0xd7448eab;
    uint16_t out = 0;

    out = in >> 10; // Shift right 10 bits
    out &= 0xffff;  // Only lower 16 bits
    printf("%x\n",out);
}
< p > in >> 10将数字向右移动10位;& 0xffff丢弃除了最低16位之外的所有位。


1
为什么要分别使用 =&=,而不是在同一语句中使用 =& - Remy Lebeau
对于C语言的初学者来说更易于理解(让我可以注释每个更改),而且&存在优先级问题(这可能会真的让不熟悉C语言的人感到困惑)。 - samiam
1
你如何调整这个解决方案以提取未知范围的位?比如一个函数extract(unsigned num, unsigned hi, unsigned lo),它返回num中范围为hilo的位? - Catherine

3
你需要使用掩码来获取所需的位。掩码是你可以使用的数字,用于以你想要的方式筛选位(保留位、删除/清除位、修改数字等)。你需要了解的是AND、OR、XOR、NOT和移位操作。对于你所需要的,只需要几个。
你知道移位: x << y 可以将位从x*y个位置向左移动。
如何按顺序设置x位为1: (1 << x) - 1 如何从y到y+x开始按顺序设置x位为1: ((1 << x) -1) << y 上述是你所需的位掩码。例如,如果你想要0xD7448EAB的16位,在10到25之间,你将需要上述掩码,其中x = 16,y = 10。
现在,只需使用AND运算符对你的数字0xD7448EAB与上述掩码进行“AND”操作,然后你就会得到所需的掩码0xD7448EAB,其中仅包含你想要的位。稍后,如果你想逐个处理每个位(位于位置0),则需要将结果向右移动10位并逐个处理。
这样的设计可能有点长,但比直接硬编码使用0xff或其他更好。

嗨,webuster,我发现你的内容比较容易理解,所以我尝试计算数值,但是输出结果似乎不正确。我在十六进制模式下使用程序员计算器。1<<16 = 400000-1= 3fffff。现在3fffff << 10 = 3fffff0000 & D7448eab = D7440000 = 11010111010001000000000000000000(二进制)。输出结果与要求不符。如果我做错了什么,请告诉我。 - user3267877
(1 << 16) - 1 = ffff。在计算器中所做的是将1左移16位,即用十六进制的22表示。尝试将1左移10位(二进制为16),你会得到正确的结果。 - webuster
我也这样做了,结果是一样的 (1<<10)-1=ffff <<10 = ffff0000 & 0xD7448EAB = D7440000 = 11010111010001000000000000000000。需要忽略前六位,如果你检查一下,它们没有被忽略,因此结果不正确。我需要的结果是1101 0001 0010 0011。 - user3267877
你一直犯着同样的错误,使用十六进制而不是十进制进行Lsh-ing和Rsh-ing。在十六进制中,使用10进行Lsh-ing意味着向左移动16位。十进制10的十六进制数字为A。因此,您应该进行的移位方式是FFFF << A。所以步骤如下(关于计算器):1 << 10,然后-1 => FFFF。然后FFFF << A = 3FFFC00。然后D7448EAB & 3FFFC00是3448C00,二进制表示为1101 0001 0010 0011,正如您所需。 - webuster
Webbuster,这非常有帮助。非常感谢。 - user3267877
显示剩余2条评论

1
我将上面的前两个答案结合起来编写了一个C程序,可以提取32位无符号整数的任意位(不仅仅是10到25位)。该函数的工作方式是返回numlohi位(包括这两位)。
#include <stdio.h>
#include <stdint.h>

unsigned extract(unsigned num, unsigned hi, unsigned lo) {
    uint32_t range = (hi - lo + 1);  //number of bits to be extracted
    //shifting a number by the number of bits it has produces inconsistent 
    //results across machines so we need a special case for extract(num, 31, 0)
    if(range == 32)
        return num;
    uint32_t result = 0;
    //following the rule above, ((1 << x) - 1) << y) makes the mask:
    uint32_t mask = ((1 << range) -1) << lo;
    //AND num and mask to get only the bits in our range
    result = num & mask;
    result = result >> lo; //gets rid of trailing 0s
    return result;
}
int main() {
    unsigned int num = 0xd7448eab;
    printf("0x%x\n", extract(num, 10, 25));
}

1
我想要第10到25位的比特。
你可以这样做:
unsigned int number = 0xD7448EAB;
unsigned int value = (number & 0x3FFFC00) >> 10;

或者这个:

unsigned int number = 0xD7448EAB;
unsigned int value = (number >> 10) & 0xFFFF;

无符号整数 number = 0xD7448EAB; unsigned int value = (number & 0x3FFFC00) >> 10; 这是完全正确的。唯一需要更改的是将>>10改为>>8。我已经解决了。谢谢 - user3267877
使用 >> 10 是你所要求的正确方式。而使用 >> 8 将会给你第8到23位上的位值,这并不是你所要求的。 - Remy Lebeau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接