Floating Point Math in Bash
When you think about it, it's surprising how many programming tasks don't require the use of floating point numbers. If you're an embedded systems programmer, you'd probably get fired for using "double" in a C program. If you write PHP or JavaScript, quick, do they even support floating point? One language that doesn't support it is Bash, but let's not let that stop us...
The obvious candidate for adding floating point capabilities to bash is bc. bc is, to quote the man page:
An arbitrary precision calculator languageAs an aside, take note of the last word in that quote: "language". That's right bc is actually a programming language, it contains if statements and while loops among others. I say as an aside because it's largely irrelevant to what we want to do today, not completely irrelevant but largely.
To use bc in our bash scripts we'll package it up into a couple of functions:
float_eval EXPRESSION
and
float_cond CONDITIONAL-EXPRESSION
Both functions expect a single floating point expression,
float_eval writes the result of the expression evaluation to standard out,
float_cond assumes the expression is a conditional expression
and sets the return/status code to zero if the expression is true
and one if it's false.
Usage is quite simple:
float_eval '12.0 / 3.0'
if float_cond '10.0 > 9.0'; then
echo 'As expected, 10.0 is greater than 9.0'
fi
a=12.0
b=3.0
c=$(float_eval "$a / $b")
The code for the functions follows:
#!/bin/bash
#
# Floating point number functions.
#####################################################################
# Default scale used by float functions.
float_scale=2
#####################################################################
# Evaluate a floating point number expression.
function float_eval()
{
local stat=0
local result=0.0
if [[ $# -gt 0 ]]; then
result=$(echo "scale=$float_scale; $*" | bc -q 2>/dev/null)
stat=$?
if [[ $stat -eq 0 && -z "$result" ]]; then stat=1; fi
fi
echo $result
return $stat
}
#####################################################################
# Evaluate a floating point number conditional expression.
function float_cond()
{
local cond=0
if [[ $# -gt 0 ]]; then
cond=$(echo "$*" | bc -q 2>/dev/null)
if [[ -z "$cond" ]]; then cond=0; fi
if [[ "$cond" != 0 && "$cond" != 1 ]]; then cond=0; fi
fi
local stat=$((cond == 0))
return $stat
}
# Test code if invoked directly.
if [[ $(basename $0 .sh) == 'float' ]]; then
# Use command line arguments if there are any.
if [[ $# -gt 0 ]]; then
echo $(float_eval $*)
else
# Turn off pathname expansion so * doesn't get expanded
set -f
e="12.5 / 3.2"
echo $e is $(float_eval "$e")
e="100.4 / 4.2 + 3.2 * 6.5"
echo $e is $(float_eval "$e")
if float_cond '10.0 > 9.3'; then
echo "10.0 is greater than 9.3"
fi
if float_cond '10.0 < 9.3'; then
echo "Oops"
else
echo "10.0 is not less than 9.3"
fi
a=12.0
b=3.0
c=$(float_eval "$a / $b")
echo "$a / $b" is $c
set +f
fi
fi
# vim: tabstop=4: shiftwidth=4: noexpandtab:
# kate: tab-width 4; indent-width 4; replace-tabs false;
The work of the functions is done by feeding the arguments to bc:
result=$(echo "scale=$float_scale; $*" | bc -q 2>/dev/null)By default bc outputs its result with no digits to the right of the decimal point and without a decimal point. To change this you have to change one of bc's builtin variables: scale. This is where the "language" features of bc are relevant, in bc as in C statements are separated by semi-colons. We set bc's scale variable by preceding the expression that we pass to bc with scale=$float_scale;. This sets the scale in bc to the value of the bash global variable float_scale, which is by default set to two (near the top of the script).
The main gotcha here has to do with the fact that "*", "<", and ">" have other meanings in bash. You can eliminate the problem of "<" and ">" by quoting your expressions, but this only works with "*" if you use single quotes and that would mean you couldn't include bash variables in the expression. The other option is to bracket your code with "set -f" and "set +f" to turn off pathname/wildcard expansion.
If you save the script as float.sh and run it directly it will execute the test code at the bottom:
$ sh float.sh 12.5 / 3.2 is 3.90 100.4 / 4.2 + 3.2 * 6.5 is 44.70 10.0 is greater than 9.3 10.0 is not less than 9.3 12.0 / 3.0 is 4.00
The one unaswered question you may have is: "and why would I want to do this?" Next time around I'll show you one place you can put this to real world use.
Mitch Frazier is an Associate Editor for Linux Journal.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.
Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.
Sponsored by ActiveState
| Non-Linux FOSS: libnotify, OS X Style | Jun 18, 2013 |
| Containers—Not Virtual Machines—Are the Future Cloud | Jun 17, 2013 |
| Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer | Jun 12, 2013 |
| Weechat, Irssi's Little Brother | Jun 11, 2013 |
| One Tail Just Isn't Enough | Jun 07, 2013 |
| Introduction to MapReduce with Hadoop on Linux | Jun 05, 2013 |
- Containers—Not Virtual Machines—Are the Future Cloud
- Non-Linux FOSS: libnotify, OS X Style
- Linux Systems Administrator
- Validate an E-Mail Address with PHP, the Right Way
- Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer
- Senior Perl Developer
- Technical Support Rep
- UX Designer
- RSS Feeds
- Introduction to MapReduce with Hadoop on Linux
- yea
1 min 6 sec ago - One advantage with VMs
2 hours 29 min ago - about info
3 hours 2 min ago - info
3 hours 3 min ago - info
3 hours 4 min ago - info
3 hours 6 min ago - info
3 hours 7 min ago - abut info
3 hours 9 min ago - info
3 hours 10 min ago - info
3 hours 12 min ago
Featured Jobs
| Linux Systems Administrator | Houston and Austin, Texas | Host Gator |
| Senior Perl Developer | Austin, Texas | Host Gator |
| Technical Support Rep | Houston and Austin, Texas | Host Gator |
| UX Designer | Austin, Texas | Host Gator |
| Web & UI Developer (JavaScript & j Query) | Austin, Texas | Host Gator |
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?



Comments
Code License?
Mitch, Thanks for the awesome floating point functions!
May I reuse this code within other scripts and projects? Which license does it carry (if any)? Thanks, JadeStar.
GPLv2
Yes, you may reuse it. License == GPLv2.
Mitch Frazier is an Associate Editor for Linux Journal.
A concise method, maybe?
This is actually my first bash script and I'm not an experienced programmer, so feel free to criticize. Interestingly, since I'm not used to programming in bash, I found a use for floating point math in my first bash script, because I didn't know it wasn't natively supported. Nevertheless, I like my solution, so far, but since I'm not my own best critic when it comes to coding, please feel free to give me some pointers. Anyway, below is the essence of the code. I'm using my floats as a counter variable, so I end up using bc both in a condition and in a calculation. (oh and thanks Mitch for giving me the essentials, so that I could strike out on my own.)
#!/bin/bash
$decrement=.1
$upperLimit=15
$lowerLimit=10
#.........omitted code.........
while (( `echo "$upperLimit >= $lowerLimit" | bc` ))
do
echo "$upperLimit" #...........altered code.........
upperLimit=`echo "$upperLimit - $decrement" | bc`
done
exit 0
Presumably not your final version
I assume this is an early version since
the following statements are not even syntactically valid:
(You don't use the $ when assigning variables).
But beyond that it looks like you might be getting there.
Mitch Frazier is an Associate Editor for Linux Journal.
I used this techique too!
I actually used this exact technique (of piping a floating point comparison into bc) pretty recently (and before reading about it here). I wanted a cron script that would monitor the CPU temp on my home server and notify me if it went too high. So I wrote a script that grabbed the output from "sensors" (from the lm-sensors package), "grep"ped for the line containing CPU temp, "cut" away all but the raw temperature value, and then piped a simple floating point comparison string into bc to compare the CPU temp vs. the threshold temp. If bc returns a 1 (true) result, I have the script spit out a message, which cron then emails to root (which is aliased to my email address). Nice little unix-esque solution of assembling a bunch of small, simple things together so as to achieve a more complex piece of behavior.
script didt run
The script simply didnt run.
ssanjayv@localhost:~/bin/my_scripts> sh float.sh
: command not found
: command not found
: command not found
: command not found
: command not found
'loat.sh: line 14: syntax error near unexpected token `
'loat.sh: line 14: `function float_eval()
any idea whats causing the problem ??
Bash??
Are you sure your shell is bash? Try running it via "sh -x float.sh".
Mitch Frazier is an Associate Editor for Linux Journal.
Can do. Should do?
If you have to do the odd bit of floating point then you might look at writing the whole script in some scripting language that supports floating point natively. It just seems error prone and fragile to pass floats as strings between bash and bc.
AWK, TCL, Python, Perl, Lua, Pike, Ruby, ... These might all be better solutions, and if your running on a striped-down, ultra small, unix platform, then it might be best to include something small but with more features like Pike or Lua rather than bc.
- Paddy.
sleep
One place I'd want them is for "sleep".
Occasionally it can be useful to sleep for a fraction of a second
(for instance when you are automatically terminating spawned processes
if they run for more than pre-specified amount of time...
see timeout.sh script in Bash examples.)
Floating points are supported by Korn shell (Bash == Korn + readline)
For instance:
echo $((1.2 + 2.9))
4.1
Why reinvent the ol' good Korn?
:-)
Also ZSH
Zsh does floating point also. I use zsh over korn since it has better line editor, cycle-tab completion (cmd.exe style), and many other nice features. But yeah, why anybody uses bash is beyond me.