Dariusz G. Jagielski
Dariusz G. Jagielski

Reputation: 665

How to encode 8 4bit values into an Int32 and then get those back?

How can I put 8 4bit (0..15 or 0..F if you like hex ;) ) values into an Int32 (int) in C# and get those back? I need these for a game that I am writing or more specifically, editor for it (which will feature vertex manipulation like in Notch's failed 0x10c) and since there's already some legacy code and few levels built, I really can't break the format so I can use Int64 and encode 8 8bit values using BitConverter class ;).

I'm not very good at bitwise ops and those who tried to explain these to me so far have failed so yeah. Need help.

Upvotes: 1

Views: 265

Answers (2)

Jim Mischel
Jim Mischel

Reputation: 134065

If you don't want to think about bitwise operations, you can set up a BitVector32, like this:

var bv = new BitVector32();
var sections = new BitVector32.Section[8];

// Create 8 4-bit sections
sections[0] = BitVector32.CreateSection(15);
for (var i = 1; i < 8; ++i)
{
    sections[i] = BitVector32.CreateSection(15, sections[i - 1]);
}

// Initialize the sections. Values will be 0F, 0D, 0B, ... ,01
for (var i = 0; i < 8; ++i)
{
    bv[sections[i]] = 15 - i*2;
}

// and output results
for (var i = 0; i < 8; ++i)
{
    Console.WriteLine("{0,2}: {1:X2}", i, bv[sections[i]]);
}
Console.WriteLine("bv value = {0:X4}", bv.Data);

After comment

The questions of "conciseness" and performance are somewhat more involved than the comment would suggest. In a working program, the part of this code that sets up the sections could be done once, at program startup. For example:

BitVector32.Section value1 = BitVector32.CreateSection(15);
BitVector32.Section value2 = BitVector32.CreateSection(15, value1);
BitVector32.Section value3 = BitVector32.CreateSection(15, value2);
// set up others here

Then, when you want to access individual values:

BitVector32 bv = new BitVector32(someValue);

// get one value
int myValue = bv[value2];

// set one value
bv[value3] = 42;

Which is more concise than:

int myValue = (someValue >> 4) & 0x0f;
someValue = (someValue & 0xfffff0ff) | (42 << 8);

Performance is an open question. It's quite possible that the JIT compiler can generate inline code for those section operations.

Upvotes: 1

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186813

The main idea is to use left << shift and | to encode and & and right >> shift to decode. Suppose the values are organized as array:

 byte[] data = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

 int result = data
   .Select((value, i) => value << (4 * i))
   .Aggregate((x, y) => x | y); // or just .Sum() - Jeppe Stig Nielsen's idea

Edit: for loop implementation:

 int result = 0;

 for (int i = 0; i < data.Length; ++i)
   result |= data[i] << (4 * i);

To retrieve the index-th value (index is in [0..7] range)

 byte item = (byte) ((result >> (index * 4)) & 0xF);

Upvotes: 7

Related Questions