Strange bit shifting result with gcc - am I missing somethingobvious?

B

Boltar

On 32 bit linux with gcc 4.2 I get unexpected results with this code:

main()
{
int bits = 32;
printf("%d\n",(int)1 << (int)32);
printf("%d\n",(int)1 << bits);
}


The first printf gives a result of 0, the second gives 1. I checked
with sizeof() and ints are definately 32 bits in size.

I'm sure I'm missing something obvious but can someone tell me what?

Thanks

B2003
 
J

jacob navia

Boltar said:
On 32 bit linux with gcc 4.2 I get unexpected results with this code:

main()
{
int bits = 32;
printf("%d\n",(int)1 << (int)32);
printf("%d\n",(int)1 << bits);
}


The first printf gives a result of 0, the second gives 1. I checked
with sizeof() and ints are definately 32 bits in size.

I'm sure I'm missing something obvious but can someone tell me what?

Thanks

B2003

When I compile tyour code with lcc-win I get:

Warning tshift.c: 2 no type specified. Defaulting to int

The prototype for main is:
int main(void)

Warning tshift.c: 5 missing prototype for printf

You did not include <stdio.h>

Warning tshift.c: 5 shift by 32 is undefined

The number of bits shifted is greater than sizeof(int)*CHAR_BIT
This is undefined!

The result of running the program is:

16777216
1

If I turn optimizations ON I get:
16777216
1024

The result of shifting more than sizeof(int)*CHAR_BIT positions
is NOT defined by the language. It is an illegal expression.
 
B

Boltar

The result of shifting more than sizeof(int)*CHAR_BIT positions
is NOT defined by the language. It is an illegal expression.

It isn't? Thats bloody annoying since that means i'll have to do a
specific check for the bit shift count being > than the size of the
type being shifted. I'd have thought a value of zero would be the
obvious default for this sort of thing , after all languages like C
are supposed to shield us from the specifics of CPU instruction
behaviour otherwise we might as well just code in assembler!

B2003
 
E

Eric Sosman

Boltar said:
It isn't? Thats bloody annoying since that means i'll have to do a
specific check for the bit shift count being > than the size of the
type being shifted.

Actually, you need to check for >=, not just >. And
with signed integers, you need to be sure the value being
shifted is non-negative and small enough that no 1's are
propagated into the sign position.
 
K

Keith Thompson

jacob navia said:
Boltar said:
On 32 bit linux with gcc 4.2 I get unexpected results with this code:

main()
{
int bits = 32;
printf("%d\n",(int)1 << (int)32);
printf("%d\n",(int)1 << bits);
}


The first printf gives a result of 0, the second gives 1. I checked
with sizeof() and ints are definately 32 bits in size.

I'm sure I'm missing something obvious but can someone tell me what?
[...]
Warning tshift.c: 5 shift by 32 is undefined

The number of bits shifted is greater than sizeof(int)*CHAR_BIT
This is undefined!
[...]

You mean greater than *or equal to*, right?

See C99 6.5.7p3:

If the value of the right operand is negative or is greater than
or equal to the width of the promoted left operand, the behavior
is undefined.

The underlying reason for this rule is that different hardware behaves
differently when shifting by a number of bits greater than or equal to
the size of the value being shifted. For example, some CPUs might
ignore the high-order bits of the shift count. By leaving the
behavior undefined, the standard allows the native shift instruction
to be used without extra fix-up code even when the value of the right
operand isn't known at compile time.
 
B

Bartc

When I compile tyour code with lcc-win I get:
Warning tshift.c: 5 shift by 32 is undefined

The number of bits shifted is greater than sizeof(int)*CHAR_BIT
This is undefined!

OK, but the following code from lccwin does not appear to set up the shift
count in cl register. So you don't even attempt to shift by 32, but by an
unknown value in cl (presumably 24)?

; 6 printf("%d\n",(int)1 << (int)32);
.line 6
movl $1,%edi
movl $32,%esi
sall %cl,%edi

On the x86 at least, shifting a 32-bit register left by 32 is anyway a
no-operation (the register is unchanged).

So the OP should not rely on 32+ bit left-shifts working as he expects.
 
J

jacob navia

Bartc said:
OK, but the following code from lccwin does not appear to set up the shift
count in cl register. So you don't even attempt to shift by 32, but by an
unknown value in cl (presumably 24)?

; 6 printf("%d\n",(int)1 << (int)32);
.line 6
movl $1,%edi
movl $32,%esi
sall %cl,%edi

On the x86 at least, shifting a 32-bit register left by 32 is anyway a
no-operation (the register is unchanged).

So the OP should not rely on 32+ bit left-shifts working as he expects.

Surely not. Things go as follows:

lcc-win discovers that we have a shift operation with
two immediate constants. This should be done at
compile time to make the generated program more efficient.

Then it discovers that one of the constants is too big
and generates a warning. Apparently, when making this
optimizations it doesn't set cl.

If you change the program to
5 printf("%d\n",(int)1 << (int)31);
.line 5
pushl $-2147483648 // Shift done at compile time
pushl $_$2
call _printf
addl $8,%esp

You see?

Now, it can be argued that this is a bug. True.

I have changed this now so that it will do the shift at compile time,
returning whatever the result is at compile time (1) anyway.
 
L

lawrence.jones

Boltar said:
On 32 bit linux with gcc 4.2 I get unexpected results with this code:

main()
{
int bits = 32;
printf("%d\n",(int)1 << (int)32);
printf("%d\n",(int)1 << bits);
}


The first printf gives a result of 0, the second gives 1. I checked
with sizeof() and ints are definately 32 bits in size.

I'm sure I'm missing something obvious but can someone tell me what?

That the shift count must be *less than* the number of bits in the
operand or you get undefined behavior.

-Larry Jones

Please tell me I'm adopted. -- Calvin
 
L

lawrence.jones

Boltar said:
It isn't? Thats bloody annoying since that means i'll have to do a
specific check for the bit shift count being > than the size of the
type being shifted.

The theory is it's better to have to have an explicit check for the
relatively small amount of code that needs it than to have the compiler
always generate for the vast majority of code that doesn't need it.

-Larry Jones

OK, what's the NEXT amendment say? I know it's in here someplace. -- Calvin
 
L

lawrence.jones

Bartc said:
On the x86 at least, shifting a 32-bit register left by 32 is anyway a
no-operation (the register is unchanged).

On the contrary, different processors in the x86 family behave
differently. Some only look at the bottom 5 bits of the count (so a
shift count of 32 is interpreted as 0 and 33 as 1) but others look at
the entire value (so a shift count of 32 or greater zeros the register).

-Larry Jones

You're just trying to get RID of me, aren't you? -- Calvin
 
G

Gordon Burditt

main()
When I compile tyour code with lcc-win I get:

Warning tshift.c: 2 no type specified. Defaulting to int

The prototype for main is:
int main(void)

Warning tshift.c: 5 missing prototype for printf

You did not include <stdio.h>

Warning tshift.c: 5 shift by 32 is undefined

The number of bits shifted is greater than sizeof(int)*CHAR_BIT
This is undefined!

Isn't the above error message misleading (assuming you're running
in a typical 32-bit setup)? The number of bits shifted is *NOT*
greater than sizeof(int)*CHAR_BIT, and the result is still undefined.

s/greater than/greater than or equal to/
 
G

Gordon Burditt

OK, but the following code from lccwin does not appear to set up the shift
Surely not. Things go as follows:

lcc-win discovers that we have a shift operation with
two immediate constants. This should be done at
compile time to make the generated program more efficient.

Then it discovers that one of the constants is too big
and generates a warning. Apparently, when making this
optimizations it doesn't set cl.

This is not a bug. 1 << 32 invokes the wrath of undefined behavior.
Since no possible answers are wrong, you might as well get one
quickly, even if it's random crap.
If you change the program to
5 printf("%d\n",(int)1 << (int)31);
.line 5
pushl $-2147483648 // Shift done at compile time
pushl $_$2
call _printf
addl $8,%esp

You see?

Now, it can be argued that this is a bug. True.

I'll argue that the wording of the error message is a
quality-of-implementation issue.
 
B

Bartc

Gordon Burditt said:
Isn't the above error message misleading (assuming you're running
in a typical 32-bit setup)? The number of bits shifted is *NOT*
greater than sizeof(int)*CHAR_BIT, and the result is still undefined.

The actual warning (not error) is:

Which is correct.

I was interested however in investigating why 1<<32 gave 16777216 and found
that slightly odd code. You could argue it doesn't matter, but just blindly
shifting the value by whatever was in a register seemed wrong. And jacob
fixed this although he wasn't asked to (and will doubtless now break some
programs :).
 
E

Ed Prochak

It isn't? Thats bloody annoying since that means i'll have to do a
specific check for the bit shift count being > than the size of the
type being shifted. I'd have thought a value of zero would be the
obvious default for this sort of thing , after all languages like C
are supposed to shield us from the specifics of CPU instruction
behaviour otherwise we might as well just code in assembler!

B2003

You mean you did not know that C is just a glorified assembler?
8^)

Ed
 
T

Tomás Ó hÉilidhe

jacob navia:
The number of bits shifted is greater than sizeof(int)*CHAR_BIT
This is undefined!


Don't forget about systems that have padding bits within their integer
types. On such systems, sizeof(some int type)*CHAR_BIT will yield a
value which is larger than the amount of value representational bits
in that type.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,151
Latest member
JaclynMarl
Top