Skip to main content
GameDev.net gamedev.net
🔒 Locked

Weird issue retrieving bits from an integer.

Started by MarkS Jul 3, 2025 at 10:11 PM 9 replies 2.3k views
Original Post
MarkS
MarkS

Another maze question, although this is more practical than the last. I'm storing both the cell data and visited cell bit flags in separate packed 64-bit integers. The cell data consists of 4 bits per cell, so I can store 16 per integer, and that is working fine. However, the visited array stores a single bit per cell and while I can set the bits just fine, retrieving them doesn't quite work and I am stumped.

The code in question is:

bool Maze::GetVisitedData(int64_t index) const
{
	if (VisitedArray == nullptr || ((index < 0) || (index > (num_rows * num_columns) - 1)))
		return false;

	// Shifting to the right by 6 chooses the correct 64-bit integer in the array.
	return VisitedArray[index >> int64_t(6)] & (index & int64_t(63));
}

It simply doesn't work. Printing the returned value either gives me 0 or 1, or, for no apparent reason, the value of the bit position. I've even tried wrapping it in a ternary operator to return true or false to no avail.

That being said, if I change it to this, it works fine:

bool Maze::GetVisitedData(int64_t index) const
{
	if (VisitedArray == nullptr || ((index < 0) || (index > (num_rows * num_columns) - 1)))
		return false;

	// Shifting to the right by 6 chooses the correct 64-bit integer in the array.
    	return std::bitset<64>(VisitedArray[index >> int64_t(6)])[index & int64_t(63)];
}

The problem is that using bitset, especially as such, is SLOOOW! I'm clearly doing something wrong, but I cannot see it.

No, I am not a professional programmer. I'm just a hobbyist having fun...
MarkS
MarkS

Nevermind… I figured it out…

(int64_t(1) << (index & int64_t(63)))

SIGH!

No, I am not a professional programmer. I'm just a hobbyist having fun...
Alberth
Alberth

Make your integers unsigned to avoid all kinds of “nice” surprises with sign bits getting duplicated.

MarkS
MarkS

Alberth said:

Make your integers unsigned to avoid all kinds of “nice” surprises with sign bits getting duplicated.

Can you explain further? I thought it wouldn't matter in this case, as I'm not doing any math on the integers.

No, I am not a professional programmer. I'm just a hobbyist having fun...
RmbRT
RmbRT

Right-shift of signed integers replicates the filled-in high bits with the sign bit. Unsigned integers will always be replenished with 0-bits while right-shifting. Also, if you insist on positive indices anyway, you can simplify the (i<0 || i >= size) with signed integers into an i >= size using an unsigned integer, as “negative” numbers have the highest bit set to 1, which is a huge positive number in unsigned integers. So “-1” in unsigned is uint64 max.

Walk with God.
MarkS
MarkS

RmbRT said:

Right-shift of signed integers replicates the filled-in high bits with the sign bit. Unsigned integers will always be replenished with 0-bits while right-shifting. Also, if you insist on positive indices anyway, you can simplify the (i<0 || i >= size) with signed integers into an i >= size using an unsigned integer, as “negative” numbers have the highest bit set to 1, which is a huge positive number in unsigned integers. So “-1” in unsigned is uint64 max.

I forgot about that! I thought it didn't matter since I was just using the integers as containers. I forgot the CPU treats them differently.

No, I am not a professional programmer. I'm just a hobbyist having fun...
frob
frob

I forgot the CPU treats them differently.

It's the compiler that treats them differently. It compiles to different machine code, with details depending on the target architecture for the compiler.

On PCs it's a cheap instruction, practically free thanks to the processor design. It is a signed shift instruction or unsigned shift instruction. For x86 that's SHR/SHL for unsigned or SAR/SAL for signed. For a Mac it's LSR/LSL for unsigned, ASR/ASL for signed. On some processors like Arduino chips that don't have a hardware barrel shifter, the compiler can require hundreds of instructions to get it done, and it can be a performance concern.

It still works the same as far as the high level code is concerned, but sometimes in game development it is important to understand seemingly simple operations can have very different performance on different architectures.

JoeJ
JoeJ

MarkS said:
It simply doesn't work.

The problem is that you convert a number made from multiple bits (x&63), into a boolean return value.
So we would assume you're interested in only one bit (e.g. x&64), which is not the case.
Thus, probably all answers you got where based on assumptions or just added some context. But we could not really guess what you tried to do (at least i can't).

Btw, i second the advise to use unsigned types for bit packing stuff. Makes life easier.

MarkS
MarkS

frob said:

I forgot the CPU treats them differently.

It's the compiler that treats them differently. It compiles to different machine code, with details depending on the target architecture for the compiler.

On PCs it's a cheap instruction, practically free thanks to the processor design. It is a signed shift instruction or unsigned shift instruction. For x86 that's SHR/SHL for unsigned or SAR/SAL for signed. For a Mac it's LSR/LSL for unsigned, ASR/ASL for signed. On some processors like Arduino chips that don't have a hardware barrel shifter, the compiler can require hundreds of instructions to get it done, and it can be a performance concern.

It still works the same as far as the high level code is concerned, but sometimes in game development it is important to understand seemingly simple operations can have very different performance on different architectures.

Sorry. That's what I meant. I was thinking about the instructions, not the fact that it's the compiler that generates them. Thanks for the correction!

No, I am not a professional programmer. I'm just a hobbyist having fun...
MarkS
MarkS

JoeJ said:

MarkS said:
It simply doesn't work.

The problem is that you convert a number made from multiple bits (x&63), into a boolean return value.
So we would assume you're interested in only one bit (e.g. x&64), which is not the case.
Thus, probably all answers you got where based on assumptions or just added some context. But we could not really guess what you tried to do (at least i can't).

Btw, i second the advise to use unsigned types for bit packing stuff. Makes life easier.

I never asked the question. I was modifying the code that I had written to extract the nibble containing the cell data. I forgot that the left shift was necessary to extract individual bits. I do very little bit manipulation in the projects that I do and this is the first serious project I've worked on in many years. I constantly need refreshers on the subject.

No, I am not a professional programmer. I'm just a hobbyist having fun...

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.