【问题标题】:Storing multiple values via bitmask in c#在c#中通过位掩码存储多个值
【发布时间】:2015-04-12 04:31:31
【问题描述】:

我正在尝试通过位掩码将四个独立的 5 位值 (0-31) 存储在 32 位 int 中,但无法正确设置值并从用于掩码的 int 中获取各个值存储。

谁能帮我解决这个问题?

编辑:

对不起,外部链接 - 这是一些 JavaScript 演示我正在尝试实现的目标(但使用位掩码而不是十进制代数):

var s = 0;

var v = [31, 6, 23, 31];

//save values
s = v[0] + (v[1] * 32) + (v[2] * 1024) + (v[3] * 32768);

console.log(s);

//retrieve values
v[3] = parseInt(s / 32768);
v[2] = parseInt((s - (v[3] * 32768)) / 1024);
v[1] = parseInt((s - ((v[3] * 32768) + (v[2] * 1024))) / 32);
v[0] = parseInt(s - ((v[3] * 32768)+ (v[2] * 1024) + (v[1] * 32)));

console.log(v);

//modify values [1] and [2]
s = s - (v[1] * 32) + (9 * 32);
s = s - (v[2] * 1024) + (17 * 1024);

console.log(s);

//retrieve values
v[3] = parseInt(s / 32768);
v[2] = parseInt((s - (v[3] * 32768)) / 1024);
v[1] = parseInt((s - ((v[3] * 32768) + (v[2] * 1024))) / 32);
v[0] = parseInt(s - ((v[3] * 32768)+ (v[2] * 1024) + (v[1] * 32)));

console.log(v);

输出:

1039583
[31, 6, 23, 31]
1033535
[31, 9, 17, 31]

编辑:

感谢 Peter Duniho,我能够使用内置的掩码来制作这些,以便在 32 位整数中保存 6 个 5 位值的一些操作:

uint Get_5_In_32(uint storage, int index)
{
    switch (index)
    {
        case 0:
            return (storage & 0x0000001F);
        case 1:
            return (storage & 0x000003E0) >> 5;
        case 2:
            return (storage & 0x00007C00) >> 10;
        case 3:
            return (storage & 0x000F8000) >> 15;
        case 4:
            return (storage & 0x01F00000) >> 20;
        case 5:
            return (storage & 0x3E000000) >> 25;
        default:
            return (0);
    }
}
uint Set_5_In_32(uint storage, uint value, int index)
{
    if (value > 31) {  value = 31; }
    switch (index)
    {
        case 0:
            return (storage & 0xFFFFFFE0) | value;
        case 1:
            return (storage & 0xFFFFFC1F) | (value << 5);
        case 2:
            return (storage & 0xFFFF83FF) | (value << 10);
        case 3:
            return (storage & 0xFFF07FFF) | (value << 15);
        case 4:
            return (storage & 0xFE0FFFFF) | (value << 20);
        case 5:
            return (storage & 0xC1FFFFFF) | (value << 25);
        default:
            return (0);
    }
}

还有一个 byref 版本的 Set 函数,分配更少:

void Set_5_In_32(ref uint storage, uint value, int index)
{
    if (value > 31) {  value = 31; }
    switch (index)
    {
        case 0:
            storage &= 0xFFFFFFE0;
            storage |= value;
            break;
        case 1:
            storage &= 0xFFFFFC1F;
            storage |= (value << 5);
            break;
        case 2:
            storage &= 0xFFFF83FF;
            storage |= (value << 10);
            break;
        case 3:
            storage &= 0xFFF07FFF;
            storage |= (value << 15);
            break;
        case 4:
            storage &= 0xFE0FFFFF;
            storage |= (value << 20);
            break;
        case 5:
            storage &= 0xC1FFFFFF;
            storage |= (value << 25);
            break;
    }
}

【问题讨论】:

  • 不,基本上我想要实现的是与jsfiddle.net/mj28pk8f/1 等效的c#,但没有算术开销(这需要运行得非常快,因为位掩码是最快的可以得到这就是我需要使用的)。外部 BitArray 类的开销太大。
  • 我不太了解 JavaScript,无法知道您拥有的代码是否是使用该语言的最佳方式。但是,我在答案中发布的代码将完成同样的事情,恕我直言,这是一种更简单、更易读、更通用的方式。
  • 当您交换班次和掩码的顺序时,您的代码将更具可读性,因为在这种情况下,掩码将始终为0x1F
  • 谢谢 - 它适用于 Get 函数,但我需要一个特定的掩码才能使其适用于 Set 函数吗?

标签: c# bitmask bit-masks


【解决方案1】:

如果没有更具体的问题,尤其是您展示了到目前为止的代码并解释了您在使其工作方面遇到的问题具体,很难确切知道是什么最好的答案是。

也就是说,这里有几个示例方法可能会让您指向正确的方向:

// Stores the given value in storage at the given index
int Set(int storage, int value, int index)
{
    int shiftCount = index * 5,
        mask = 0x1f << shiftCount;

    return (storage & ~mask) | (value << shiftCount);
}

// Retrieves the value stored in storage at the given index
int Get(int storage, int index)
{
    int shiftCount = index * 5,
        mask = 0x1f << shiftCount;

    return (storage & mask) >> shiftCount;
}

上面的Set() 方法采用storage 中的当前值,清除要存储五位值的位范围内的所有位,然后使用| 运算符来存储该值五位值,首先将该值的位移动到正确的位置。

Get() 方法执行相反的操作。它屏蔽(清除)存储值的位范围内的所有位 not,然后将存储的位向下移动到 int 的最低有效五位,然后返回结果。

注意事项:

  • 以上内容针对您所陈述的问题。它可以通过封装在一个可以在初始化时配置位计数的类中轻松概括,并且掩码是基于该位计数而不是硬编码生成的。
  • 上述代码中没有错误检查。在生产代码版本中,最好验证传递给Set() 方法的value 确实适合五位(即小于0x20)。

编辑:

这是一个简单的控制台程序,演示了上述内容的用法,以及您的示例数据:

static void Main(string[] args)
{
    int[] array = { 31, 6, 23, 31 };
    int storage = 0;

    storage = ArrayToStorage(array, storage);

    Console.WriteLine(storage);
    LogArray(array);

    storage = Set(storage, 9, 1);
    storage = Set(storage, 17, 2);

    StorageToArray(array, storage);

    Console.WriteLine(storage);
    LogArray(array);
}

static int ArrayToStorage(int[] array, int storage)
{
    for (int i = 0; i < array.Length; i++)
    {
        storage = Set(storage, array[i], i);
    }

    return storage;
}

static void StorageToArray(int[] array, int storage)
{
    for (int i = 0; i < array.Length; i++)
    {
        array[i] = Get(storage, i);
    }
}

static void LogArray(int[] array)
{
    Console.WriteLine("[" + string.Join(", ", array) + "]");
}

// Stores the given value in storage at the given index
static int Set(int storage, int value, int index)
{
    int shiftCount = index * 5,
        mask = 0x1f << shiftCount;

    return (storage & ~mask) | (value << shiftCount);
}

// Retrieves the value stored in storage at the given index
static int Get(int storage, int index)
{
    int shiftCount = index * 5,
        mask = 0x1f << shiftCount;

    return (storage & mask) >> shiftCount;
}

【讨论】:

  • 谢谢,这正是我想要的。
猜你喜欢
  • 2023-03-15
  • 1970-01-01
  • 2012-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-27
相关资源
最近更新 更多