0

I wrote the following and I expected that 16 would be printed.

#include <iostream>

enum E : long { e = 16 };

struct X 
{
    E e : 5;
};

X x;


int main(){ x.e = E::e; std::cout << static_cast<int>(x.e) << std::endl; }

DEMO

But it wasn't. I got a compiler warning and -16 was printed instead. The warning was:

warning: implicit truncation from 'E' to bitfield changes value from 16 to -16 

It's unclear to me. Why was the warning display and why was -16 printed? I declared the bit-field of size of 5 that's enough to store 16 in there.

Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
  • 2
    5 bits is not enough to hold 16 if the type is signed. Change the enum's underlying type to `unsigned` and your code should work. – Praetorian Dec 19 '14 at 06:32
  • 1
    I think you want `unsigned long` ... http://coliru.stacked-crooked.com/a/6a1576333fd7ec55 – l'L'l Dec 19 '14 at 06:47

1 Answers1

1

It is a two's complement issue with signed values. You're going out of range of what a 5-bit signed value can represent.

If you only have 5 bits to store the value of 16, you'll have 10000. The leading 1 indicates that this is a negative value. Only 4 bits represent the magnitude when you have 5 bits for a signed value. To determine the absolute value of a 2s complement value, you flip all the bits and add 1, so

10000 -> 01111 -> 10000 which is 16, so it's negative 16.

Your options would be to use 6 bits instead of 5 if you want to represent the signed range of values, or use an unsigned long in which case you can use all 5 bits for the magnitude

Community
  • 1
  • 1
Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
  • Thanks for your response, but as far as I know it's implementation defined if either two's complement or signed magnitude is used. Right? –  Dec 19 '14 at 06:39
  • More precisely _this International Standard permits 2’s complement, 1’s complement and signed magnitude representations for integral types._ from 3.9.1/8 –  Dec 19 '14 at 06:41
  • if you use `long` then it's a signed value. C++ doesn't require that signed values be implemented as two's complement, but you are out of the range of values that can be represented by this value. If you use `unsigned long` it will be an unsigned value. – Ryan Haining Dec 19 '14 at 06:41
  • You're getting `-16` because you're using two's complement. signed integer overflow is undefined by the standard so you could get `0` for all you know, the compiler knows what's going on though and is telling you what it's actually doing to cope – Ryan Haining Dec 19 '14 at 06:42
  • _because you're using two's complement_ Why that? _C++ doesn't require that signed values be implemented as two's complement_ I haven't come across the requirements, couldn't you get the reference? –  Dec 19 '14 at 06:44
  • You're using two's complement because that's what your hardware is using. It's true that C++ doesn't require two's complement but none of the representations you mention will be able to hold 16. – Ryan Haining Dec 19 '14 at 06:49
  • what I'm saying is that once you exceed the maximum value `X::e` can hold for positives, C++ doesn't care what happens. However there is an implementation-specific reason you are specifically getting 16, which is what I was explaining. Unless I'm misunderstanding your confusion – Ryan Haining Dec 19 '14 at 06:50