473,494 Members | 2,223 Online
Bytes | Software Development & Data Engineering Community
Create Post

Home Posts Topics Members FAQ

Bit-fields and integral promotion/UACs

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
Jan 27 '06 #1
10 1648
"Christian Kandeler" <ch****************@hob.de> wrote in message
news:43*************@individual.net...
#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
Jan 27 '06 #2
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/>
Jan 27 '06 #3
In article <43*************@individual.net>
Christian Kandeler <ch****************@hob.de> 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.
Jan 27 '06 #4
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
Jan 27 '06 #5
Christian Kandeler <ch*******@kandeler.de> 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) ks***@mib.org <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.
Jan 27 '06 #6
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 ---
Jan 27 '06 #7
"CBFalconer" <cb********@yahoo.com> wrote in message
news:43***************@yahoo.com...
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
Jan 28 '06 #8
Alex Fraser wrote:
"CBFalconer" <cb********@yahoo.com> 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
Jan 28 '06 #9
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
Jan 28 '06 #10
"CBFalconer" <cb********@yahoo.com> wrote in message
news:43***************@yahoo.com...
Alex Fraser wrote:
"CBFalconer" <cb********@yahoo.com> wrote in message [snip]
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.


Pardon?
And why did you remove the corrected code that didn't care how many bits
were in an int?


Because it is irrelevant to the behaviour of the code above when the number
of bits in an int is known, which is the reason for this thread.

Alex
Jan 28 '06 #11

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

13
3392
by: Amy DBA | last post by:
I've been asked to administer a DB2 V 8 (32-bit install) on a Solaris 64-bit platform. It seems like whomever installed DB2 on the server, goofed for not installing DB2 v8 64 bit. Do I understand...
12
6329
by: Jean-Marc Blaise | last post by:
Hi, Is it worth to use 64-bit DB2 instances on a 32-bit kernel, in terms of: - performance - configuration (go beyond the 256 Mb segment for private mem, 1.75 Gb for Bufferpools) - other ? ...
11
1874
by: JDeats | last post by:
1. Will there be different 64-bit .NET implementations for Intel and AMD 64-bit processors or will they share a common 64-bit CLR? 2. Will .NET managed code compiled for the 32-bit CLR be binary...
58
30155
by: Larry David | last post by:
Ok, first of all, let's get the obvious stuff out of the way. I'm an idiot. So please indulge me for a moment. Consider it an act of "community service".... What does "64bit" mean to your friendly...
4
3821
by: tommydkat | last post by:
Well, I've finally gotten UDB 8.2 FixPak 3 up and running on my HP-UX 11i system, thanks to Mr McBride and IBM support. :) I created a 32-bit instance and that's running just fine. However, I...
14
11891
by: ern | last post by:
Does a function exist to convert a 128-bit hex number to a string?
14
11021
by: =?Utf-8?B?R2Vvcmdl?= | last post by:
Hello everyone, I am using C# to develop DLL using Visual Studio 2005 and .Net 2.0, and I have no idea of how to make my DLL work with applications on 64-bit platform. Above all, I do not...
1
5499
by: Chuck Chopp | last post by:
I have some code that is being built on the following: Windows Server 2003, both 32-bit & 64-bit editions Windows Vista, both 32-bit & 64-bit editions Windows Server 2008, both 32-bit & 64-bit...
0
7157
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
7195
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
1
6873
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
7367
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
1
4889
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
4579
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
0
3078
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
644
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
285
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.