27

I'm curious as to what happens when a numeric variable in bash is incremented up without purposely stopping it. How large can the number get? Will it overflow and become negative and just continue to increment forever? Will it break and skid to a stop at some point?

I am using an x86_64 AMD processor, but I would be happy to hear 32bit answers as well, just specify which you are talking about. I am running Fedora21 64bit.

I've googled far and wide but haven't found this specific tidbit for some odd reason. It seems like it would be a basic piece of info in all the manuals and such.

3
  • 3
    How about printing out some powers of 2 as a starter: for i in {0..70}; do echo 2 to the power of $i = $((2**i)); done
    – mpy
    Jan 22, 2016 at 21:19
  • 1
    If you want big numbers, you might switch to ksh which does floating point arithmetics, not integer one like bash: ksh -c 'echo $((2**1023))'8.98846567431157954e+307
    – jlliagre
    Jan 22, 2016 at 21:36
  • 1
    I will keep ksh in mind if I need floating point or values in the stratosphere, floating point can be quite useful. But this question is simply so that I know the limits of my system, it is not because I need to exceed its limits. I will do something like mpy suggests, I didn't to start with because I didn't want risk causing a system crash.
    – Max Power
    Jan 23, 2016 at 0:27

5 Answers 5

33

It may come down to your version of bash, your OS, and your CPU architecture. Why don't you try it yourself? Set a variable to (2^31)-1, then increment it, set it to 2^32, then increment it, set it to 2^64, then increment it, etc.

Here, I just tried it myself on my Core i7 Mac running OS X "El Capitan" v10.11.3, and it looks like bash is using signed 64-bit integers.

$ uname -a
Darwin Spiff.local 15.3.0 Darwin Kernel Version 15.3.0: Thu Dec 10 18:40:58 PST 2015; root:xnu-3248.30.4~1/RELEASE_X86_64 x86_64
$ bash --version
bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)
Copyright (C) 2007 Free Software Foundation, Inc.
$
$ ((X=2**16)); echo $X
65536                        <-- okay, at least UInt16
$ ((X=2**32)); echo $X
4294967296                   <-- okay, at least UInt32
$ ((X=2**64)); echo $X
0                            <-- oops, not UInt64
$ ((X=(2**63)-1)); echo $X
9223372036854775807          <-- okay, at least SInt64
$ ((X++)); echo $X
-9223372036854775808         <-- overflowed and wrapped negative. Must be SInt64
3

I set up a loop. while return status is 0 increment a variable with addition and print the variable to stdout I started it just under 2^31, I let it pass both 2^31 and 2^32 with no issue, stopped it, then set the initial value to just under 2^63. The result was that it seamlessly rolled over from 9.22e18 to -9.22e18 and continued to increment positively. (toward zero)

Just to check that my while [ $? -eq 0 ] was actually using the exit status of the commands within the while loop, not using the exit status of the previous script or some oddity, I also ran it with an extra command inside the loop designed to create a non zero exit status at particular increments.

So it is signed, it will roll over rather than stop at max value, and it does so without any error message. So it is possible to end up in a truly infinite loop. It does not limit the 64bit hardware and 64bit linux OS to an old 16 or 32bit standard.

3

I improved and automated @Spiff 's answer:

MAX_INT=0
for ((x=1,y=1; x<<=y; y<<=1))
do
    if ((x>0))
    then
        MAX_INT=$x
        continue
    fi

    if (((x=~x)>0))
    then
        MAX_INT=$x
    fi
    break
done
echo $MAX_INT
2

bash use 64-bit integer. So if you increase after variable reaching its max number, variable will overflow. Below is my test with unsigned int and signed integer.

MAX_UINT = 18446744073709551615
MAX_INT = 9223372036854775807

$ printf "%llu\n" $((2**64))
0
$ printf "%llu\n" $((2**64-1))
18446744073709551615

$ printf "%lld\n" $((2**63-1))
9223372036854775807
$ printf "%lld\n" $((2**63))
-9223372036854775808
$ printf "%lld\n" $((2**64-1))
-1
3
  • 2
    please add some text explaining how this answers the question. It looks like you are trying to demonstrate the upper limits via code - can you explain why this code will achieve the required outcome? Jul 13, 2018 at 6:15
  • I think the point is that we dont know that we can use 64 and we need to figure it out (unless you can link to a spec?). To get rid of this you can use these two; printf "%llu\n" $(((~0))) for unsigned and printf "%llu / 2\n" $(((~0))) | bc -l | grep -oP '^\d+' for signed MAX's. But now the point becomes "how did I know my system supported %llu?"
    – Hashbrown
    Apr 20, 2020 at 10:34
  • @Hashbrown I think bash's printf will do %lu as %llu, but also can probably have a separate test that returns the number of bits: X=$(printf "%llx" -1) && echo "$((${#X}*4))"
    – nhed
    May 6, 2022 at 19:42
1

commands to get maximum bash integer (maxI), and minimum bash integer (minI):

$ maxI=$( echo `printf "%u" -1` / 2  | bc )
$ echo $maxI
9223372036854775807
$ minI=$(($maxI + 1))
$ echo $minI
-9223372036854775808

The results in different machines may be different, but the command should be the same.

-- Update --

Shorter maxI (remove the redundant "echo"), and more "exact" one:

$ maxI=$( printf "%u/2\n" -2  | bc )
$ echo $maxI
9223372036854775807

Use -2 than -1 is a bit more exact, where, printf "%u" -1 gives me 18446744073709551615 on my machine which is not divisible by 2, although bc gives the correct answer 9223372036854775807, since bc truncate anything after the decimal point. (since the default 'scale' variable is zero). Using -2, it would be now 18446744073709551614, which is divisible by 2, and gives the correct answer when divided.

1
  • Best answer so far!
    – Smeterlink
    Sep 7, 2022 at 14:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.