Home > Mobile >  Comparing multiple bits between two uint64_t's based on unique type id always returns true
Comparing multiple bits between two uint64_t's based on unique type id always returns true

Time:05-06

I've got a map where keys are entity id's (uint32_t) and values are "signatures," aka uint64_t's with certain bits set that represent a type: std::unordered_map<uint32_t, uint64_t> m_entityComponents{};

My goal is to write a template function that accepts a variable amount of types and returns whether or not ALL of those types exist inside an entity. Let's call that function bool hasAllComponents(uint32_t entity).

To do this, I've written a method which returns a unique id for each type:

template <class T>
uint8_t getTypeId()
{
    static uint8_t s_type =   m_nextComponentType;
    return s_type;
}

And then, given any number of types, I construct a new "signature" by calling getTypeId() using a fold expression to set each bit for every type present to 1:

template<typename... Types>
uint64_t getTypesSignature()
{
    return ((1 << getTypeId<Types>()) | ... | 0);
}

Given these two helper functions, I should be able to check if an entity in my map has all of the passed in types with my hasAllComponents method, which uses a bitwise & operator to compare the cached signature in the map with the newly created one:

template<typename... Types>
bool hasAllComponents(uint32_t entity)
{
    uint64_t signature = getTypesSignature<Types...>();
    return (m_entityComponents[entity] & signature);
}

However, hasAllComponents still returns true when only some of the bits match, not all. I think the issue might be with my getTypesSignature fold logic not setting all bits correctly, although printing out the results looks fine to me. There's a flaw in my logic somewhere and would appreciate any help tracking it down!

CodePudding user response:

You are generating a signature that contains all the required bits set to 1, but you are not correctly checking if the entity's value actually has all of those same bits set to 1. You are checking if the value has any of those same bits is set to 1 instead.

In this comparison:

return (m_entityComponents[entity] & signature);

The result of the & operator will be a uint64_t, which is implicitly convertible to bool, so the return value will be true if the resulting uint64_t is non-zero, otherwise it will be false.

So, let's say the calculated signature is 107 (b01101011). That means any combination of values with any of those bits set will result in true, eg:

1:   b00000001 & b01101011 = b00000001, != 0? true
2:   b00000010 & b01101011 = b00000010, != 0? true
4:   b00000100 & b01101011 = b00000000, != 0? false
8:   b00001000 & b01101011 = b00001000, != 0? true
16:  b00010000 & b01101011 = b00000000, != 0? false
32:  b00100000 & b01101011 = b00100000, != 0? true
64:  b01000000 & b01101011 = b01000000, != 0? true
107: b01101011 & b01101011 = b01101011, != 0? true
128: b10000000 & b01101011 = b00000000, != 0? false
255: b11111111 & b01101011 = b01101011, != 0? true

To make sure the value has all of the required bits are set, you simply need to change the comparison to this instead:

return ((m_entityComponents[entity] & signature) == signature);

The result will be true only if the value has at least all of the signature's bits set (it can have more), eg:

1:   b00000001 & b01101011 = b00000001, == b01101011? false
2:   b00000010 & b01101011 = b00000010, == b01101011? false
4:   b00000100 & b01101011 = b00000000, == b01101011? false
8:   b00001000 & b01101011 = b00001000, == b01101011? false
16:  b00010000 & b01101011 = b00000000, == b01101011? false
32:  b00100000 & b01101011 = b00100000, == b01101011? false
64:  b01000000 & b01101011 = b01000000, == b01101011? false
107: b01101011 & b01101011 = b01101011, == b01101011? true
128: b10000000 & b01101011 = b00000000, == b01101011? false
255: b11111111 & b01101011 = b01101011, == b01101011? true
  • Related