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
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Designing Electronics with Linux | May 22, 2013 |
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
- New Products
- Linux Systems Administrator
- Senior Perl Developer
- Technical Support Rep
- UX Designer
- Web & UI Developer (JavaScript & j Query)
- Designing Electronics with Linux
- Dynamic DNS—an Object Lesson in Problem Solving
- Using Salt Stack and Vagrant for Drupal Development
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Joomla Installation Service
11 hours 43 min ago - features
17 hours 3 min ago - Reply to comment | Linux Journal
1 day 3 hours ago - Nice article, thanks for the
1 day 14 hours ago - I once had a better way I
1 day 20 hours ago - Not only you I too assumed
1 day 20 hours ago - another very interesting
1 day 22 hours ago - Reply to comment | Linux Journal
2 days 12 min ago - Reply to comment | Linux Journal
2 days 7 hours ago - Reply to comment | Linux Journal
2 days 7 hours ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
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.