October 9, 2005

Decimal Math in shell scripts

I was reading about inserting random delays in cron jobs and was surprised when I saw this code to get a random number:
# Grab a random value between 0-240.
value=$RANDOM
while [ $value -gt 240 ] ; do
value=$RANDOM
done

I thought, there’s got to be a better way to get a random number within a specific range. I mean, that kind of seems like a waste of CPU cycles, particularly when $RANDOM can be anywhere in between 0 and 32767. And a quick test verified that was exactly the case. On average (out of 500 samples), using that method to get a random number below 240 requires 134 attempts. Not a horrid performance impact, but enough to get my anal retentive brain in a twist.

My first thought was to use the expr command to do some simple math to properly adjust the $RANDOM variable into the desired range. But soon enough, I realized the expr only deals with integers…bummer. Looking around to see what else I could use for decimal math, I eventually came across the dc command - a reverse-polish desk calculator:echo -e "6\nk\n$RANDOM\n32767\n/\n1\nk\n240\n*\np" | dc | cut -f1 -d.

This definitely seems to be faster.

time echo -e “6\nk\n$RANDOM\n32767\n/\n1\nk\n240\n*\np” | dc | cut -f1 -d.
104

real 0m0.003s
user 0m0.003s
sys 0m0.002s

COUNT=0; time while [ $RANDOM -gt 240 ]; do COUNT=`expr $COUNT + 1`; done; echo $COUNT

real 0m0.445s
user 0m0.158s
sys 0m0.297s
295

Update: After pondering the comment below, I realized this can be done in bash by using factors of 100 to resolve the integer math issue. There are several ways to do this, but here are a couple options:

RAND1=`expr $RANDOM \* 100`; FACTOR=`expr $RAND1 \/ 32767`; DIV=`expr $FACTOR \* 240`; RESULT=`expr $DIV \/ 100`;
or
expr $(expr $(expr $(expr 9264 \* 100) \/ 32767) \* 240) \/ 100

They appear to take about the same time, around 0.050s real time and 0.015s sys time.

2 Comments so far
Leave a comment

Just a quickie on why the variable was chosen, even if slightly longer compute. $RANDOM was chosen because it’s part of bash; when dealing cross-platform (Solaris, AIX, Linux), we found that bash was always a common package loaded.

Ah, very good point. I had never even heard of dc prior to playing with this and was surprised to find that bash could only do integer math. Thanks for the update!



Leave a comment
Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>