How to set/check msb on a generalized inttype?

0

I have the following snippets somewhere in my current code base:

// uint32_t id; defined somewhere
bool msb_set = id & 0x80000000

And would like to change this to something more flexible:

using id_t = uint64_t;
id_t id;
bool msb_set = id & 0x80000000  // uh-oh, semantic change for any other differently sized inttype

How can I most easily generate the appropriate literal depending on the concrete inttype I'm using? I'm looking for sth along the lines of

numeric_constants<id_t>::int_with_msb_set();
c++
templates
bit-manipulation
asked on Stack Overflow Feb 5, 2019 by user1709708

2 Answers

1

You can leverage a std::bitset to check if the most significant bit is set. You can convert the number to a std::bitset and then return the most significant bit like

template<typename T> 
constexpr bool mb_set(T value)
{
    constexpr auto size = sizeof(T) * CHAR_BIT;
    return std::bitset<size>(value)[size - 1];
}

testing this on clang with -O2 or -O3 optimizes the function away to a shr instruction.

answered on Stack Overflow Feb 5, 2019 by NathanOliver • edited Feb 5, 2019 by NathanOliver
1

Here is a function that returns the masks you want:

template<typename T>
constexpr T getMsbMask()
{
    if constexpr (std::is_unsigned_v<T>)
        return T(-1) - T(-1) / 2;
    else
        return std::numeric_limits<T>::min();
    // Unsigned branch computes 0b11111... - 0b01111...
    // Signed branch assumes two's complement, which will soon be the standard:
    // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r0.html
}

Demo and tests on godbolt.

The compiler can evidently figure out these constant values at compile-time, so there is no speed penalty whatsoever.

The other obvious solution would just be a struct with specializations for each type (akin to std::numeric_limits).

answered on Stack Overflow Feb 5, 2019 by Max Langhof • edited Feb 5, 2019 by Max Langhof

User contributions licensed under CC BY-SA 3.0