• Advertisement

• ### Popular Now

• 12
• 12
• 9
• 10
• 13
• Advertisement
• Advertisement
• Advertisement

# Replacing range of bits on int

This topic is 1802 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

## Recommended Posts

Hello,

so I have an int64 as a "sort key", where I set specific bits to group my render calls into e.g. materials from bit 34 to bit 48. Now I wonder, what was an optimized way to set all bits in this range? Seems my bit-math is still a bit off, I can't think of any of the standard opertations to perform this. I could bitshift every single bit from my material id, and then eigther set or unset the respective bit in the sort-key, but there's gotta be a better way to replace all bits from X to Y in an int64 with the respective bits of another int64. Right?

void Model::SetMaterial(unsigned int id)
{
//that does only set the bit, but what about already set ones that should be overwritten?
//plus, it would overide all bits up to #64, but we only want this to be up to #48
m_sortKey |= ((unsigned __int64)(m_pMaterial->m_id) << 34);
}

Edited by Juliean

#### Share this post

##### Share on other sites
Advertisement

Clear the old bits before OR:ing in the new value.

Sweet, that did the job! Thanks +1

#### Share this post

##### Share on other sites

Another item to consider is using bit fields.  Often a bit field clears up a lot of confusion while still maintaining the performance.  There are some down sides of course but they can generally be worked around with a union.  I.e.:

union
{
uint64_t    YouDataType;
struct
{
unsigned LowerBits : 16;
unsigned HighLow : 16;
unsigned PaddingInt64Bits : 31;
unsigned HighBit : 1;
};
};


Just pass things around as the underlying 64 bit type and stash it in temporaries of the union type when manipulating it.  The compilers will do all the bit twiddles for you and generally remove the temporary so you won't see any performance differences.  Of course "generally" is a bad word, so test it on your compiler. :)

#### Share this post

##### Share on other sites

Ah, thats actually a lot nicer, since it feels more "OOP" than fiddeling around with bit operators (did I already mentioned I didn't like bit artitmetics? No? well I should have... :/ ). Finally get to use unions too, never though how and when to learn them properly haha. As for performance, I'm sure going to profile it, but in comparison to whats going on in my render queue, its probably not such a great deal, even if there was a penalty on this.

EDIT: one more question though, how can I safely initialize all bits to 0? Do I have to call each "attribute" of the struct individually, because this left both Unused and Layer untouched:

//only "clears" material and depth
m_sortKey.bits = 0;

union Key64
{
unsigned __int64 bits;
struct
{
unsigned Material : 16;
unsigned Depth : 32;
unsigned Unused2 : 14;
unsigned Layer : 2;
};
};


Edited by Juliean

#### Share this post

##### Share on other sites

Ah, thats actually a lot nicer, since it feels more "OOP" than fiddeling around with bit operators (did I already mentioned I didn't like bit artitmetics? No? well I should have... :/ ). Finally get to use unions too, never though how and when to learn them properly haha. As for performance, I'm sure going to profile it, but in comparison to whats going on in my render queue, its probably not such a great deal, even if there was a penalty on this.

EDIT: one more question though, how can I safely initialize all bits to 0? Do I have to call each "attribute" of the struct individually, because this left both Unused and Layer untouched:

//only "clears" material and depth
m_sortKey.bits = 0;

union Key64
{
unsigned __int64 bits;
struct
{
unsigned Material : 16;
unsigned Depth : 32;
unsigned Unused2 : 14;
unsigned Layer : 2;
};
};



Simple:

Key64 temp;

temp.bits = 0;

Assumption though, your bit field does not exceed the size of the int64, which in this case is true.

#### Share this post

##### Share on other sites

Simple:

Key64 temp;

temp.bits = 0;

Assumption though, your bit field does not exceed the size of the int64, which in this case is true.

Thats just how I did it (see first two liens of sample code, maybe should have declared that variable here first), it seems that "not clearing" of unused and layer was just an display error of some sort, "bits" indeed was 0.

#### Share this post

##### Share on other sites

Simple:

Key64 temp;

temp.bits = 0;

Assumption though, your bit field does not exceed the size of the int64, which in this case is true.

Thats just how I did it (see first two liens of sample code, maybe should have declared that variable here first), it seems that "not clearing" of unused and layer was just an display error of some sort, "bits" indeed was 0.

Just for safety, you might want to do a quick assertion to verify things:

assert( sizeof( Key64 )==sizeof( __int64 ) );

Looking at things, this should be true but double checking is always a good idea.

Oops, just noticed a gotcha.  You are trying to pack the 32 bit depth at a midpoint of your fields it is likely getting packed in the next 32bit value.  So your bitfield is likely going to be 3 32 bit items instead of the pair you were hoping for.  Gotta double check this, but pretty sure that is true.

Double checked: this is likely going to be the problem.  The underlying type is not large enough for the 32 bit item so it packs into the next full 32 bit item.  If you change to an 'unsigned long long' it should go back to what you desire.  (C99 only allows up to int/unsigned int, C++ allows any integral type to be used as the underlying type.)

Couple edits later, think I have this all correct now.

union Key64
{
unsigned __int64 bits;
struct
{
unsigned long long Material : 16;
unsigned long long Depth : 32;

unsigned long long Unused2 : 14;

unsigned long long Layer : 2;

};
};

Should fix the problem and also be valid C++.

Edited by Hiwas

#### Share this post

##### Share on other sites

Couple edits later, think I have this all correct now. smile.png

union Key64
{
unsigned __int64 bits;
struct
{
unsigned long long Material : 16;
unsigned long long Depth : 32;

unsigned long long Unused2 : 14;

unsigned long long Layer : 2;

};
};

Should fix the problem and also be valid C++.

Okay, thanks, would have never quessed that! But isn't this going to mean that the union will have 4 times long long size, or is it just some sort of hint to the compiler how he should pack those bits?

#### Share this post

##### Share on other sites

Should fix the problem and also be valid C++.

Okay, thanks, would have never quessed that! But isn't this going to mean that the union will have 4 times long long size, or is it just some sort of hint to the compiler how he should pack those bits?

Nope, it should turn out the same size as the uint64 you want.  Basically the bit field will steal bits from the underlying type till it runs out.  So it takes 16, then 32, then 14 and then 2, which means it never ran out of bits so it just uses the single uint64.  Throw that assert in I mentioned and it should verify the behavior for you.

#### Share this post

##### Share on other sites

• Advertisement