0

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
3
  • There's really no reason to use a bitfield here. On modern platforms that structure will get rounded up to 32-bits anyway. Also masking and two's-complement aren't compatible that way, the sign bit will get ripped off. Commented Jan 30, 2017 at 17:23
  • @tadman, I understand masking with MAX_VALUE rips of the sign bit. But how can I fix that? Masking with the MASK value results in a conversion warning. Commented Jan 30, 2017 at 17:27
  • I think @kamajii here has some great advice and I'd encourage you to follow it. Commented Jan 30, 2017 at 17:41

1 Answer 1

3

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.
Sign up to request clarification or add additional context in comments.

2 Comments

Reading this it looks really bad what I'm doing, I'll think about taking a different approach without bit-fields. My goal was not to get good performance, but to simulate a different device that computes with certain restrictions on the number of bits (so simulation, not performance). But this doesn't really matter to your argument. I'll think about how I can change it.
Are you sure my MIN_VALUE was one off? It seems correct to me, there should be one more negative value.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.