Search code examples
c++bit-fields

constructing signed bit-fields


Below I constructed a small example of some code I'm working on that uses bit fields. When implementing the comparison operator, I noticed it did not work as expected. The problem being that -1 does not seem to be less than 1.

After thinking about this for a while, it looks to me there is something wrong with the masking in the constructor.

I added the "aValue & MAX_VALUE" there because otherwise the compiler gives a conversion warning. But, first masking with MAX_VALUE, and then casting to an int32_t does not give me the right result.

Does anyone know how I can fix this? I do seem to get my expected result when I write minusOne.v = -1; in the main() method. But I'm not sure how I could write that in the constructor (without getting the conversion warning).

#include <iostream>
#include <cstdint>

using namespace std;

struct int27_t
{
    int32_t v:27;

    constexpr static int32_t MIN_VALUE = -(1L << 26);
    constexpr static int32_t MAX_VALUE = (1L << 26) - 1;
    constexpr static int32_t MASK = 0x7FFFFFF;

    int27_t() : v(0) {}

    explicit int27_t(int32_t aValue) : v(static_cast<int32_t>(aValue & MAX_VALUE)) {}

    friend bool operator<(const int27_t& aLhs, const int27_t& aRhs);
};

bool operator<(const int27_t& aLhs, const int27_t& aRhs)
{
    return aLhs.v < aRhs.v;
}

int main()
{
    int27_t minusOne{-1};
    int27_t plusOne{1};
    cout << "MAX_VALUE == " << int27_t::MAX_VALUE << " == 0x" << hex << int27_t::MAX_VALUE << dec << endl;
    cout << "-1 == " << minusOne.v << " == 0x" << hex << minusOne.v << dec << endl;
    cout << "-1 cast to int32_t: " << static_cast<int32_t>(minusOne.v) << " == 0x" << hex << static_cast<int32_t>(minusOne.v) << dec << endl;
    cout << "-1 < 1 ? " << (minusOne < plusOne) << endl;
    cout << endl;
}

Program output

MAX_VALUE == 67108863 == 0x3ffffff
-1 == 67108863 == 0x3ffffff
-1 cast to int32_t: 67108863 == 0x3ffffff
-1 < 1 ? 0

Solution

  • Boolean arithmetic does not work that way on signed types. You are simply &-ing away the sign in your constructor.

    There are basically four issues with your code:

    1. Do not use binary arithmetic on signed types. Just don't do it. If really necessary, arithmetically convert to unsigned (multiply by -1 and be aware of overflow), do something and get back to signed arithmetically.
    2. You are relying on a certain representation of signed numbers. As far as I know (which is C99 in what concerns signed types), the language offers three distinct ones: sign and magnitude, one's complement and two's complement, cf. ISO/IEC 9899:TC2 §6.2.6.2-2.
    3. Assuming two's complement, which should hold for almost all recent platforms, your MIN_VALUE is off by one.
    4. Don't use bit-fields, please. Almost any recent architecture will expand it to some word-sized type. You probably don't gain anything but trouble because signed overflow is undefined behaviour. So there's no point in restricting a signed type's range this way.