如何在C语言中从一个数字中提取特定的位?

21

我需要在C语言中从short数据类型中提取特定部分(位数)。

例如,我有一个二进制数52504,表示为1100110100011000,我想要前6位(LSB-->MSB,即011000十进制24)和后面的10位(11001101000十进制820)。

同样地,我想要这个函数过于通用,以提取给定“开始”和“结束”(即等效于某些十进制值的一些位块)的特定位数。

我查看了其他帖子,但它们没有提供足够通用的函数。

我需要适用于C语言的short数据类型的解决方案。

编辑

我拥有2048字节大小的短数组。每个像素是10位。因此,我的16位字节包含2个像素数据的时间,有时是3个像素数据。

例如:

(像素:0,1) 10位+ 6位

然后(像素:1,2,3) 4位(第一个像素剩余的位)+ 10位+ 2位。

这种模式继续下去...所以,我想要提取每个像素,并使整个数组具有让每个像素完全占据一个完整字节(16位)的每个像素,例如:

一个字节应该包含一个数据像素,另一个字节应该包含另一个像素值的整个16位,依此类推。


4
只需屏蔽您想要的部分,例如num & 0000000000111111;或者如果您需要另一端,则使用(num & 1111111111000000) >> 6 - Ferguzz
@hexa:你可能是对的,但这是一个建设性的问题。该怎么办呢...?该怎么办呢...? - Nathan Fellman
请问您能否让问题更具体一些?您先说您想将两个数字分成两部分,然后又说您想指定一个起始和结束,这是否意味着您想要输出3个数字?即顶部、中间和底部? - SpacedMonkey
实际上,我正在处理一个巨大的数组,大小为2048,其中包含从相机拍摄的图像中某些像素的特定值。 - Usman
C类型并不保证大小。short可能有18位或32位,或者符合C标准的任何值。您需要指定特定的大小,或使用uint8_t等带有尺寸的整数。 - phuclv
8个回答

29

构建这个算法需要了解两个基本概念:

  • 获取最低的 N 位需要构造一个 比特掩码,在末尾有 N 个二进制位为 1。方法如下:((1 << N)-1)。其中 1 << N 表示左移 N 位,结果为 2 ^ N:它在第 N+1 个位置上有一个二进制位为 1,其他位置为 0。减去一得到你需要的掩码。
  • 舍弃最低的 M 位可以通过简单地向右移动进行: k >> M

现在将从 MN 的算法变成一个两步过程:先将原始值向右移动 M 位,然后与 N-M1 的掩码执行按位 AND 操作。

#define LAST(k,n) ((k) & ((1<<(n))-1))
#define MID(k,m,n) LAST((k)>>(m),((n)-(m)))

int main() {
    int a = 0xdeadbeef;
    printf("%x\n",  MID(a,4,16));
    return 0;
}

这段代码从第4位(包括)到第16位(不包括)裁剪数据,并在运行时打印bee。位从零开始编号。


17
unsigned short extract(unsigned short value, int begin, int end)
{
    unsigned short mask = (1 << (end - begin)) - 1;
    return (value >> begin) & mask;
}

请注意,[begin, end) 是一个半开区间。


11

可以这样做:

mask = ~(~0 << (end - start + 1));
value = (n >> start) & mask;

其中n是原始整数,value是提取的位。

mask的构造如下:

1. ~0 = 1111 1111 1111 1111 1111 1111 1111 1111
2. ~0 << (end - start + 1) = 1111 1111 1111 1111 1100 0000 0000 0000
   // assuming we are extracting 14 bits, the +1 is added for inclusive selection
   // ensure that end >= start
3. ~(~0 << (end - start + 1)) = 0000 0000 0000 0000 0011 1111 1111 1111

现在,将变量n向右移动start位,以将所需的位对齐到左侧。然后进行按位与运算并得到结果。


返回错误的值.. 例如..我有一个数150,它的二进制是0000000010010110..我需要从开始位置(2-5)的位..并且它们加起来是22。 但这根本不起作用。 - Usman
@Usman22 是最右边的 5 个位...你做对了吗? - Sufian Latif
当然可以,你可以选择前5位或者从第2位到第6位,就像我之前说的一样。它计数到22。所以,我给出了起始值为1和结束值为5,或者你可以将结束值设为6...但必须是22。但是它没有返回22,而是在起始值为1和结束值为5时返回11。 - Usman
我不明白。 我只需要二进制位的特定部分。 我认为我已经编辑了帖子,使其更清晰。:-) - Usman
@Usman 但是第2到5位怎么会变成22呢?我也不理解。第2到5位是0101,对吧?0000000010[0101]10 <- 选择正确吗? - Sufian Latif
显示剩余3条评论

3
//To get value from specific position 'pos' to 'pos+offset' in number 'value'

#define bitGet(value, offset, pos) (((1ull << offset) - 1) & (value >> (pos - 1)))

//Set value 'newval' from position 'pos' to 'pos+offset' in number 'value'

#define bitSet(value, offset, pos, newval)  \
(~(((1ull << offset) - 1) << (pos - 1)) & value) | ((((1ull << offset) - 1) & newval) << (pos - 1))

2
尽管这是一个非常老的问题,但我想添加一个不同的解决方案。可以使用宏,
/* 这里, startBit : 开始位位置(从LSB计数) endBit : 结束位位置(从LSB计数) .注意:endBit>startBit number : 要提取位的数字 maxLength:数字的总位数。 */
#include <stdio.h>
#define getnbits(startBit,endBit,number,maxLength) \
  ( number &  ( (~0U >> (maxLength-endBit)) & (~0U << startBit) )  ) 

int main()
{
    unsigned int num=255;
    unsigned int start=1,end=5,size=sizeof(num)*8;

    printf("Inputs : %d %d %d %d \n ",start,end,num,size);
    printf("Input number : %d\n",num);

    if(end>start)
    {
        int result = getnbits(start,end,num,size-1);
        printf("Output : %u\n\n",result);
    }
    else
        printf("Error : EndBit is smaller than starBit!\n\n");

    return 0;
}

输出: 输入:1 5 255 32
输入数字:255
输出:62

这里,255 = 11111111,而62 = 00111110


1
// This is the main project file for VC++ application project 
// generated using an Application Wizard.

#include "stdafx.h"

#using <mscorlib.dll>

using namespace System;


void fun2(int *parr)
{
    printf(" size of array is %d\n",sizeof(parr));
}
void fun1(void)
{
    int arr[100];
    printf(" size of array is %d\n",sizeof(arr));
    fun2(arr);
}

int extractBit(int byte, int pos) 
{
    if( !((pos >= 0) && (pos < 16)) )
    {
        return 0;
    }
    return ( ( byte & (1<<pos) ) >> pos);
}
int extractBitRange(int byte, int startingPos, int offset) 
{


   if(  !(((startingPos + offset) >= 0) && ( (startingPos + offset) < 16)) )
   {
        return 0;
   }
   return ( byte >> startingPos ) & ~(0xff << (offset + 1));
}

int _tmain()
{
    // TODO: Please replace the sample code below with your own.

    int value;
    signed int res,bit;
    signed int stPos, len;
    value = 0x1155;
    printf("%x\n",value);
    //Console::WriteLine("Hello World");
    //fun1();
    for(bit=15;bit>=0;bit--)
    {
        res =extractBit(value,bit);
        printf("%d",res);
    }
    stPos = 4;
    len = 5;
    res = extractBitRange(value, stPos, len);
    printf("\n%x",res);

    return 0;
}

0
void  f(short int last, short int first, short int myNr){
      //construct mask for last bits
      short int mask=0;
      for(int i=0;i<last;i++)
       { mask+=1;
        mask<<1;}
      short int aux= myNr;
      aux=aux&mask; // only last bits are left
      //construct mask for first bits
      mask=0;
      for(int i=0;i<first;i++)
       { mask+=0x8000h;
        mask>>1;} 
      aux=myNr;  
      aux&=mask;
      aux>>last; // only first bits are left and shifted
}

你可以添加参数来获取值或其他内容


0
unsigned int extract_n2mbits(unsigned int x, int n, int m)
{
unsigned int mask, tmp;
if (n < m) {
    n = n + m;
    m = n - m;
    n = n - m;
}
mask = 1 << (n - m + 1);
tmp = m;
while (tmp > 1) {
    mask = mask << 1 | 1 << (n - m + 1);
    tmp = tmp - 1;
}
return ((x & mask) >> (n - m + 1));
}

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