如何检查整数中是否只设置了一位?

I want to write a function HasOneBit, that

  • accepts any integer type (signed or unsigned, 8 to 64 bits),
  • is constexpr,
  • does not invoke undefined behaviour.

我试图对此概括:

bool HasOneBit (std::uint64_t value)
{
    return value != 0 && (value & (value - 1)) == 0;
}

This underflows, if type of value would be a signed integer, and we passed the smallest value to the function. Do I have to overload the function 8 times to implement all possibilities?

评论
  • 芦苇F浅
    芦苇F浅 回复

    The following template function satisfies all criteria (live demo):

    template <class T>
    constexpr bool HasOneBit (T value)
    {
        static_assert (std::is_integral<T>::value && !std::is_same<T, bool>::value,
                       "This function should be used only with integers.");
    
        const std::make_unsigned_t<T> unsignedValue = value;
    
        return unsignedValue != 0 && (unsignedValue & (unsignedValue - 1)) == 0;
    }
    

    This won't invoke undefined behaviour, because value is first converted to the unsigned counterpart of T. This conversion does not change the bit representation of value.

    I think, the relevant quote from the standard is this (see N4713, [conv.integral]#2):

    如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模2n,其中n是用于表示无符号类型的位数)。 [注:在二进制补码表示中,此转换是概念性的,并且位模式没有任何变化(如果没有截断)。 —尾注]

    A newer version of this rule is even simpler. Not sure, whether this applies to the unsigned to signed conversion as well.

    否则,结果是目标类型的唯一值,该唯一值与源整数模2N一致,其中N是目标类型的宽度。