Bitwise shift and the largest integer in Bash
Bash uses intmax_t
variables for arithmetic. On your system these are 64 bits in length, so:
$ echo $((1<<62))
4611686018427387904
which is
100000000000000000000000000000000000000000000000000000000000000
in binary (1 followed by 62 0s). Shift that again:
$ echo $((1<<63))
-9223372036854775808
which is
1000000000000000000000000000000000000000000000000000000000000000
in binary (63 0s), in two's complement arithmetic.
To get the biggest representable integer, you need to subtract 1:
$ echo $(((1<<63)-1))
9223372036854775807
which is
111111111111111111111111111111111111111111111111111111111111111
in binary.
As pointed out in ilkkachu's answer, shifting takes the offset modulo 64 on 64-bit x86 CPUs (whether using RCL
or SHL
), which explains the behaviour you're seeing:
$ echo $((1<<64))
1
is equivalent to $((1<<0))
. Thus $((1<<1025))
is $((1<<1))
, $((1<<1026))
is $((1<<2))
...
You'll find the type definitions and maximum values in stdint.h
; on your system:
/* Largest integral types. */
#if __WORDSIZE == 64
typedef long int intmax_t;
typedef unsigned long int uintmax_t;
#else
__extension__
typedef long long int intmax_t;
__extension__
typedef unsigned long long int uintmax_t;
#endif
/* Minimum for largest signed integral type. */
# define INTMAX_MIN (-__INT64_C(9223372036854775807)-1)
/* Maximum for largest signed integral type. */
# define INTMAX_MAX (__INT64_C(9223372036854775807))
From the CHANGES
file for bash
2.05b:
j. The shell now performs arithmetic in the largest integer size the machine supports (intmax_t), instead of long.
On x86_64 machines intmax_t
corresponds to signed 64-bits integers. So you get meaningful values between -2^63
and 2^63-1
. Outside that range you just get wrap-arounds.
Shifting by 1024 gives a one, because the shift amount is effectively taken modulo the number of bits (64), so 1024 === 64 === 0
, and 1025 === 65 === 1
.
Shifting something other than a 1
makes it clear it's not a bit rotation, as the higher bits don't wrap around to the low end before the shift value is (at least) 64:
$ printf "%x\n" $(( 5 << 63 )) $(( 5 << 64 ))
8000000000000000
5
It may be that this behaviour depends on the system. The bash code Stephen linked to shows just a plain shift, without any checking for the right hand value. If I remember correctly, x86 processors only use the bottom six bits of the shift value (in 64-bit mode), so the behaviour may be directly from the machine language. Also, I think shifts by more than the bit width aren't clearly defined in C either (gcc
warns for that).