Хранение нескольких значений с помощью битовой маски в C#
Я пытаюсь сохранить четыре независимых 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]
Редактировать:
Благодаря Питеру Дунихо я смог сделать это с помощью встроенных масок, чтобы сохранить некоторые операции для 6 5-битных значений внутри 32-битного целого числа:
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);
}
}
И еще одна версия функции 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;
}
}
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()
Метод выполняет обратную операцию. Он маскирует (очищает) все биты, не входящие в диапазон битов, в которых было сохранено значение, а затем сдвигает сохраненные биты до наименее значимых пяти битов int
прежде чем вернуть этот результат.
Заметки:
- Вышеуказанное относится к вашей заявленной проблеме. Это может быть легко обобщено путем инкапсуляции в классе, где счетчик битов может быть сконфигурирован при инициализации, и маска генерируется на основе этого количества битов, а не жестко кодируется.
- В приведенном выше коде нет проверки ошибок. В версии с производственным кодом было бы гораздо лучше проверить, что
value
перешел кSet()
метод на самом деле вписывается в пять битов (т.е. меньше, чем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;
}