1

Say I have a bitfield like this:

struct SomeStruct {
    uint32_t first : 12;
    uint32_t second : 2;
    uint32_t third : 18;
};

SomeStruct obj;

I want to assign

obj.second = 3;  // Actually, the maximum allowed value

What is the portable way to achieve this? I also don't want to use known bit field width explicitly.

My code used

obj.second = std::numeric_limits<uint32_t>::max()

but clang with -Wbitfield-constant-conversion gives a warning about it and cpp reference states:

The following properties of bit fields are implementation-defined The value that results from assigning or initializing a bit field with a value out of range, or from incrementing a bit field past its range.

So is assigning -1 or numeric_limits<uint32_t>::max() actually portable or is there any other way?

unkulunkulu
  • 11,576
  • 2
  • 31
  • 49
  • related/dupe: http://stackoverflow.com/questions/3319717/is-there-a-bit-equivalent-of-sizeof-in-c and http://stackoverflow.com/questions/539251/getting-the-size-of-an-indiviual-field-from-a-c-struct-field – NathanOliver Mar 18 '16 at 11:59
  • @NathanOliver, so do you imply that the answer to my question is "it's impossible?" – unkulunkulu Mar 18 '16 at 13:22
  • Well if you wanted to get the size in bits to calculate a max then yes. It depends what you want to do. That is why I went with related first. – NathanOliver Mar 18 '16 at 13:30
  • I want to assign a maximum value. Linked questions imply that it's not possible via getting the size in bits first, ok. Interesting point is that getting the bit size could be possible via getting the max value though :) – unkulunkulu Mar 18 '16 at 13:50

5 Answers5

2

If you have two bits, the max value is 22-1. One simple way to get that value is by shifting 1 two steps to the left (giving you 22) and subtracting 1.

So the max value for 2 bits is (1U << 2) - 1.

It is portable between all C compilers, and the compiler will be able to optimize the operation so the assignment is only for the resulting value.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • ok, portable true but I want this assignment to be stable and work should the bit field width change. I will add this to the question – unkulunkulu Mar 18 '16 at 12:39
2

Since the fields are unsigned, the job is easy:

obj.second = -1;

Unsigned arithmetic is well-defined to wrap around, even for bitfields.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • This is the optimal answer – Angus Comber Mar 18 '16 at 12:12
  • Could you elaborate a bit more with a link to the standard/other credible source as I managed to find this http://en.cppreference.com/w/cpp/language/bit_field and there it's stated that 'The following properties of bit fields are implementation-defined The value that results from assigning or initializing a bit field with a value out of range, or from incrementing a bit field past its range.' – unkulunkulu Mar 18 '16 at 12:42
  • @unkulunkulu I don't see any text in the C++ standard corresponding to that quote. There's nothing in the bit-fields section that makes an exception for their behaviour on out-of-range conversion;. so I think [conv.integral] applies, which is the same behaviour for other integer types: out-of-range conversion to unsigned wraps around, and out-of-range conversion to signed is implementation-defined. – M.M Mar 18 '16 at 13:07
  • ok, I should then really ask a separate question about the clang warning and accept this answer. Thank you! – unkulunkulu Mar 18 '16 at 13:34
  • or actually it would really be just the same question. I'm confused now. Clang with -Wbitfield-constant-conversion will just give me warning/errors on trying to do this assignment. – unkulunkulu Mar 18 '16 at 13:48
  • @unkulunkulu cpprefernce may be using **[conv.integral]/4** *If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.* for there rational on setting to a value bigger than it can hold. – NathanOliver Mar 18 '16 at 14:02
  • @NathanOliver yeah, in this code we have unsigned bitfields so that wouldn't apply – M.M Mar 18 '16 at 22:26
  • @NathanOliver CWG 1816 https://wg21.cmeerw.net/cwg/issue1816 makes it pretty clear that assigning an out-of-range value is implementation defined for both signed and unsigned bit-fields. – cmeerw Apr 19 '18 at 19:25
2

CWG 1816 makes it clear that assigning a value that is not representable by the bit-field, the resulting value is implementation defined.

I am afraid, I don't know any portable way to achieve what you want.

cmeerw
  • 7,176
  • 33
  • 27
0

The best way is assigning it -1/-1L/~0/~0L or INT_MAX/LONG_MAX.

But this code relies on the way how integers are represented in nowadays computers. This is working everywhere but formally from C standard specification is not portable.

Zbynek Vyskovsky - kvr000
  • 18,186
  • 3
  • 35
  • 43
  • `~0` relies on 2's complement representation – M.M Mar 18 '16 at 12:04
  • @M.M : That's correct. But when assigning a number to smaller type, C standard says that it uses modulo. In other words if the number wasn't power of 2, it would result in some weird number. Though all the current software relies on that quite a lot... – Zbynek Vyskovsky - kvr000 Mar 18 '16 at 12:09
-1

For unsigned integral types:

unsigned char max = ~0;

In English, complement of zero sets all bits to 1 which is the maximum unsigned value for the type.

As you are using bitfields this simple approach may not work for you. If you were using:

struct SomeStruct {
    uint8_t first;
    uint8_t second;
    uint8_t third;
};

Then it could be an option.

Angus Comber
  • 9,316
  • 14
  • 59
  • 107