Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Bit-fields and integral promotion/UACs

Reply
Thread Tools

Bit-fields and integral promotion/UACs

 
 
Christian Kandeler
Guest
Posts: n/a
 
      01-27-2006
Hi,

consider the following program:

#include <stdio.h>

int main(void)
{
struct test {
unsigned int x : 1;
} test;

test.x = 1;

printf("%lu\n", (unsigned long) (test.x << 31));

return 0;
}

On a platform with 64-bit longs and 32-bit ints, this prints
18446744071562067968, i.e. a number that has the upper 33 bits set to 1.
This stunned me at first, but I think I have now figured out what happens:

(1) Because the bit-field is only one bit wide, all its values fit into a
signed int, so test.x is converted to one.
(2) Therefore, the result of the shift operation is a signed int too.
(3) Since the resulting value is negative on this platform, ULONG_MAX + 1 is
added to it, yielding the value mentioned above.

Is this correct?
If it is, the (unwanted) sign extension is the result of (1), which converts
the unsigned bit-field to a signed int. This could then easily be avoided
by casting the bit-field to an unsigned int before the shift. However, the
resulting program

#include <stdio.h>

int main(void)
{
struct test {
unsigned int x : 1;
} test;

test.x = 1;

printf("%lu\n", (unsigned long) ((unsigned int) test.x << 31));
return 0;
}

still prints the same value with gcc 3.3.3. All other compilers I have tried
(including gcc 4), print 2147483648, as I had originally expected. Is my
assumption correct that gcc 3 is wrong here? Or am I overlooking something
and the behavior is actually implementation-defined?


Thanks,
Christian


 
Reply With Quote
 
 
 
 
Alex Fraser
Guest
Posts: n/a
 
      01-27-2006
"Christian Kandeler" <> wrote in message
news:...
> #include <stdio.h>
>
> int main(void)
> {
> struct test {
> unsigned int x : 1;
> } test;
>
> test.x = 1;
>
> printf("%lu\n", (unsigned long) (test.x << 31));
>
> return 0;
> }
>
> On a platform with 64-bit longs and 32-bit ints, this prints
> 18446744071562067968, i.e. a number that has the upper 33 bits set to 1.
> This stunned me at first, but I think I have now figured out what
> happens:
>
> (1) Because the bit-field is only one bit wide, all its values fit into a
> signed int, so test.x is converted to one.


Yes (I would say "the value of test.x is converted"); this is integer
promotion.

> (2) Therefore, the result of the shift operation is a signed int too.


Yes, because the type of the result of a shift is always the same as the
(promoted) left-hand operand.

> (3) Since the resulting value is negative on this platform, ULONG_MAX + 1
> is added to it, yielding the value mentioned above.


The shift invokes undefined behaviour, because the left-hand operand has
signed type and the result cannot be represented in that type.

[snip]
> This could then easily be avoided by casting the bit-field to an unsigned
> int before the shift. However, the resulting program

[snip: previous code with cast added]
> still prints the same value with gcc 3.3.3. All other compilers I have
> tried (including gcc 4), print 2147483648, as I had originally expected.
> Is my assumption correct that gcc 3 is wrong here?


By my understanding, yes.

Alex


 
Reply With Quote
 
 
 
 
CBFalconer
Guest
Posts: n/a
 
      01-27-2006
Alex Fraser wrote:
>

.... snip ...
>
>> This could then easily be avoided by casting the bit-field to an
>> unsigned int before the shift. However, the resulting program

>
> [snip: previous code with cast added]
>
>> still prints the same value with gcc 3.3.3. All other compilers
>> I have tried (including gcc 4), print 2147483648, as I had
>> originally expected. Is my assumption correct that gcc 3 is
>> wrong here?

>
> By my understanding, yes.


Since you snipped the (faulty) code with added cast, it is hard to
criticize. Anyway I have added that (tautened) code back below:

#include <stdio.h>
int main(void) {
struct test {
unsigned int x : 1;
} test;

test.x = 1;
printf("%lu\n", (unsigned long) ((unsigned int) test.x << 31));
return 0;
}

Try:
printf("%lu\n", ((unsigned long) test.x) << 31);

The first task is to get the value 1 into unsigned long form.
After that the shift can function without overflow. In part this
is the fault of the standards attitude towards value preservation,
when unsigned preservation would be more appropriate.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>


 
Reply With Quote
 
Chris Torek
Guest
Posts: n/a
 
      01-27-2006
In article <>
Christian Kandeler <> wrote:
[code snipped]

>On a platform with 64-bit longs and 32-bit ints, this prints
>18446744071562067968, i.e. a number that has the upper 33 bits set to 1.
>This stunned me at first, but I think I have now figured out what happens:
>
>(1) Because the bit-field is only one bit wide, all its values fit into a
>signed int, so test.x is converted to one.
>(2) Therefore, the result of the shift operation is a signed int too.
>(3) Since the resulting value is negative on this platform, ULONG_MAX + 1 is
>added to it, yielding the value mentioned above.
>
>Is this correct?


Yes.

ANSI/ISO C has the "wrong" rules (according to me anyway ) for
handling mixes of signed and unsigned. The "right" rule is very
simple: "if any operand is unsigned, the result is unsigned."
This rule is simple and easy to understand, but sometimes gives
"surprising" results.

The ISO rule is: "If any operand is unsigned, it is widened, but
the resulting type depends on the possible ranges of values of the
original unsigned type and the wider type." This rule is complicated
and hard to understand, and *still* sometimes gives surprising
results. (Moreover, the results depend on the relative values of
the various *_MAXes, for non-bitfield types. In particular,
implementations with a USHRT_MAX of 65535 and an INT_MAX of 32767
behave differently from those with a USHRT_MAX of 65535 and an
INT_MAX of 2147483647, when doing arithmetic with "unsigned short".)

Because we are stuck with the horrible, near-impossible-to-reason-about,
implementation-dependent "value preserving" rules (am I laying it
on a little thick? ), your only recourses are intermediate
temporary variables or casts.

>... This could then easily be avoided by casting the bit-field to
>an unsigned int before the shift. However, the resulting program
>
>#include <stdio.h>
>
>int main(void)
>{
> struct test {
> unsigned int x : 1;
> } test;
>
> test.x = 1;
>
> printf("%lu\n", (unsigned long) ((unsigned int) test.x << 31));
> return 0;
>}
>
>still prints the same value with gcc 3.3.3. All other compilers I have tried
>(including gcc 4), print 2147483648, as I had originally expected. Is my
>assumption correct that gcc 3 is wrong here?


Gcc 3.3.3 is wrong here.

(Note that shifting an "unsigned int" 31 bits is itself at least
a little risky, since there are 16-bit "int" implementations.)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
 
Reply With Quote
 
Christian Kandeler
Guest
Posts: n/a
 
      01-27-2006
Alex Fraser wrote:

> The shift invokes undefined behaviour, because the left-hand operand has
> signed type and the result cannot be represented in that type.


Don't you mean "cannot _necessarily_ be represented"? Surely if int is 32
bits, then 1 << 31 is okay? Or, more generally, 1 << sizeof int * CHAR_BIT
- 1 is? If not, I'd be interested to know why.


Christian
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      01-27-2006
Christian Kandeler <> writes:
> Alex Fraser wrote:
>
>> The shift invokes undefined behaviour, because the left-hand operand has
>> signed type and the result cannot be represented in that type.

>
> Don't you mean "cannot _necessarily_ be represented"? Surely if int is 32
> bits, then 1 << 31 is okay? Or, more generally, 1 << sizeof int * CHAR_BIT
> - 1 is? If not, I'd be interested to know why.


If int is 32 bits, the INT_MAX is 2147483647.

1 << 31 is 2147483648.

--
Keith Thompson (The_Other_Keith) kst- <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
 
Reply With Quote
 
Joe Wright
Guest
Posts: n/a
 
      01-27-2006
Christian Kandeler wrote:
> Alex Fraser wrote:
>
>
>>The shift invokes undefined behaviour, because the left-hand operand has
>>signed type and the result cannot be represented in that type.

>
>
> Don't you mean "cannot _necessarily_ be represented"? Surely if int is 32
> bits, then 1 << 31 is okay? Or, more generally, 1 << sizeof int * CHAR_BIT
> - 1 is? If not, I'd be interested to know why.
>
>
> Christian


It's the old off-by-one problem.
1 << 31 yields..
1000 0000 0000 0000 0000 0000 0000 0000 Min int = -2147483648
...and MAX_INT is..
0111 1111 1111 1111 1111 1111 1111 1111 Max int = 2147483647

--
Joe Wright
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
 
Reply With Quote
 
Alex Fraser
Guest
Posts: n/a
 
      01-28-2006
"CBFalconer" <> wrote in message
news:...
> Alex Fraser wrote:

[snip]
> >> Is my assumption correct that gcc 3 is wrong here?

> >
> > By my understanding, yes.

>
> Since you snipped the (faulty) code with added cast, it is hard to
> criticize. Anyway I have added that (tautened) code back below:
>
> #include <stdio.h>
> int main(void) {
> struct test {
> unsigned int x : 1;
> } test;
>
> test.x = 1;
> printf("%lu\n", (unsigned long) ((unsigned int) test.x << 31));
> return 0;
> }


The OP stated 32-bit ints, so the value of "(unsinged int) text.x << 31" can
be represented in the result type, unsigned int.

Alex


 
Reply With Quote
 
CBFalconer
Guest
Posts: n/a
 
      01-28-2006
Alex Fraser wrote:
> "CBFalconer" <> wrote in message
>> Alex Fraser wrote:

> [snip]
>>>> Is my assumption correct that gcc 3 is wrong here?
>>>
>>> By my understanding, yes.

>>
>> Since you snipped the (faulty) code with added cast, it is hard to
>> criticize. Anyway I have added that (tautened) code back below:
>>
>> #include <stdio.h>
>> int main(void) {
>> struct test {
>> unsigned int x : 1;
>> } test;
>>
>> test.x = 1;
>> printf("%lu\n", (unsigned long) ((unsigned int) test.x << 31));
>> return 0;
>> }

>
> The OP stated 32-bit ints, so the value of "(unsinged int) text.x
> << 31" can be represented in the result type, unsigned int.


No it can't by that code. And why did you remove the corrected
code that didn't care how many bits were in an int? Together with
an explanation of why it was needed.

--
"The power of the Executive to cast a man into prison without
formulating any charge known to the law, and particularly to
deny him the judgement of his peers, is in the highest degree
odious and is the foundation of all totalitarian government
whether Nazi or Communist." -- W. Churchill, Nov 21, 1943


 
Reply With Quote
 
Christian Kandeler
Guest
Posts: n/a
 
      01-28-2006
Joe Wright wrote:

>> Surely if int is 32 bits, then 1 << 31 is okay? [ ... ]
>>

> It's the old off-by-one problem.
> 1 << 31 yields..
> 1000 0000 0000 0000 0000 0000 0000 0000 Min int = -2147483648


I knew that the shift yielded a negative value. However, another look at the
standard showed that this is not guaranteed at all:

[E1 << E2]
If E1 has a signed type and nonnegative value, and E1 × 2^E2 is
representable in the result type, then that is the resulting value;
otherwise, the behavior is undefined.

This is the part that I was not aware of; I naively assumed that since we
don't exceed a width of 32 bits, the operation had to be valid.


Christian


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
C/C++ language proposal: Change the 'case expression' from "integral constant-expression" to "integral expression" Adem C++ 42 11-04-2008 12:39 PM
C/C++ language proposal: Change the 'case expression' from "integral constant-expression" to "integral expression" Adem C Programming 45 11-04-2008 12:39 PM
Minimum sizes of integral and floating point types Ioannis Vranos C++ 13 03-10-2008 06:29 PM
Bit-fields and integral promotion Carsten Hansen C Programming 117 02-08-2005 12:22 AM
size and nomenclature of integral types Shailesh C++ 4 04-04-2004 06:37 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57