Bit shifting past length of type

B

British0zzy

Is it a defined operation when I do something like this:
char a = 15;
a = a<<9;
 
B

British0zzy

Richard said:
British0zzy said:


Only on systems where char has at least 10 bits.

The relevant Standard cite is 3.3.7 Bitwise shift operators:

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

Is there any system out there which will not zero out all the bits?
 
N

Nate Eldredge

British0zzy said:
Is there any system out there which will not zero out all the bits?

Yes.

nate@archdiocese:/tmp$ uname -a
Linux archdiocese 2.6.23 #2 PREEMPT Fri Jan 25 19:38:09 PST 2008 i686 GNU/Linux
nate@archdiocese:/tmp$ gcc -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.3.2-1' --with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --enable-targets=all --enable-cld --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.3.2 (Debian 4.3.2-1)
nate@archdiocese:/tmp$ cat s2.c
#include <stdio.h>

unsigned int shift(unsigned int a, unsigned int c) { return a << c; }

int main(void) {
printf("%d\n", shift(5, 40));
return 0;
}

nate@archdiocese:/tmp$ gcc s2.c
nate@archdiocese:/tmp$ ./a.out
1280
nate@archdiocese:/tmp$ gcc -O2 s2.c
nate@archdiocese:/tmp$ ./a.out
0

For those not familiar with the 386 and its successors, unsigned int
is 32 bits.

The 386 provides an instruction which shifts a 32-bit register. The
count of bits to shift is contained in an 8-bit register. In order
not to do an unnecessary amount of shifting, the instruction masks off
the highest 3 bits of the count to get a value between 0 and 31. gcc
implements the << operator directly in terms of this instruction, so
the same phenomenon appears. The only way to avoid it would be by
generating a test whether the count was greater than 32, which would
require an extra compare and conditional jump and be a lot less
efficient. So gcc takes advantage of the fact that "undefined
behavior" allows it to use the native instruction's behavior.

With optimization on, gcc is able to compute the result of the shift
at compile time, and does it the "right" way.

The same effect doesn't appear with char on this platform, but you get
the idea.
 
B

British0zzy

Yes.

nate@archdiocese:/tmp$ uname -a
Linux archdiocese 2.6.23 #2 PREEMPT Fri Jan 25 19:38:09 PST 2008 i686 GNU/Linux
nate@archdiocese:/tmp$ gcc -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.3.2-1' --with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --enable-targets=all --enable-cld --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.3.2 (Debian 4.3.2-1)
nate@archdiocese:/tmp$ cat s2.c
#include <stdio.h>

unsigned int shift(unsigned int a, unsigned int c) { return a << c; }

int main(void) {
        printf("%d\n", shift(5, 40));
        return 0;

}

nate@archdiocese:/tmp$ gcc s2.c
nate@archdiocese:/tmp$ ./a.out
1280
nate@archdiocese:/tmp$ gcc -O2 s2.c
nate@archdiocese:/tmp$ ./a.out
0

For those not familiar with the 386 and its successors, unsigned int
is 32 bits.

The 386 provides an instruction which shifts a 32-bit register.  The
count of bits to shift is contained in an 8-bit register.  In order
not to do an unnecessary amount of shifting, the instruction masks off
the highest 3 bits of the count to get a value between 0 and 31.  gcc
implements the << operator directly in terms of this instruction, so
the same phenomenon appears.  The only way to avoid it would be by
generating a test whether the count was greater than 32, which would
require an extra compare and conditional jump and be a lot less
efficient.  So gcc takes advantage of the fact that "undefined
behavior" allows it to use the native instruction's behavior.

With optimization on, gcc is able to compute the result of the shift
at compile time, and does it the "right" way.

The same effect doesn't appear with char on this platform, but you get
the idea.

Ok, Thanks!
 
P

Peter Nilsson

Richard Heathfield said:
British0zzy said:

Only on systems where char has at least 10 bits.

The relevant Standard cite is 3.3.7 Bitwise shift
operators:

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

That is almost incidental. The promoted type must have
a width of at least 16.

The problem is that plain char may be signed or
otherwise promote to a narrow int.

n1256: 6.5.7p4

The result of E1 << E2 is E1 left-shifted E2 bit
positions; vacated bits are filled with zeros. ...
If E1 has a signed type and nonnegative value,
and E1x2^E2 is representable in the result type,
then that is the resulting value; otherwise, the
behavior is undefined.

If CHAR_MAX == INT_MAX, then even a shift of 1
could invoke undefined behaviour.

There is also a problem with the assignment if the
shifted value _is_ representable as an int, but
outside the range of plain char.

Neither C90 nor C99 guarantee truncation for narrowing
of signed types. Under C99, an implementation defined
signal may even be raised.

As disasterous as this all sounds, the solution
is very simple. Where possible, stick to unsigned
types for bitwise operations. It is rarely impractical
to do this.
 
P

Peter Nilsson

British0zzy said:
Is there any system out there which will not zero out
all the bits?

Why don't you state what you're trying to do, rather than
asking if code which you _think_ is the solution to an
(undisclosed problem) is valid?

You should be shifting unsigned types where possible.
It's unusual to shift wider than given type, but not
that unusual to shift _as wide_ as the given type.

If w is the width of an unsigned type, it's easy to
shift by 0..w bits, where a shift of w zeros out the
field...

/* shift by n bits: 0 <= n <= width of u */
if (w) u = u << (n - 1) << 1;

Note that 'width' means the number of (sign plus)
value bits, not necessarily the full size of the
type in bits.
 
P

Peter Nilsson

Eric Sosman said:
...The value of CHAR_BIT affects the outcome because
it affects the value of CHAR_MAX used in the conversion,
but it does not affect the operation of or validity of
the shift.

... changing the second line to `int i = a << 9;'
yields a fragment whose behavior is the same on all
implementations.

If CHAR_MAX <= INT_MAX and a > (INT_MAX >> 9), then the
behaviour of a << 9 is undefined.
 
C

CBFalconer

British0zzy said:
Is it a defined operation when I do something like this:
char a = 15;
a = a<<9;

That depends on the value of MAX_CHAR. Compare it with 512 * 15.
 
P

Peter Nilsson

Jack Klein said:
What exactly do you mean by "promote to a narrower int"?

Since I didn't write that, I can't answer you. :)

What I mean by 'narrow int' is that if plain char promotes
to int, then int must be at least 9 bits wider than plain
char for a left shift of 9 bits to be guaranteed to work
work for arbitrary char values. [Obviously, there is still
the problem of assigning the shifted value back to char.]
 

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
474,436
Messages
2,571,696
Members
48,796
Latest member
Greg L.
Top