Industry guru Dave Taylor answers free tech support questions about a wide variety of business and technical topics, including blogging, Google AdSense, MySpace, Sony PSP, Apple iPod, Mp3 players, management, Linux, SEO, Mac OS X, Facebook, Twitter, LinkedIn and Microsoft Windows.

How do I compare float / real numbers in a shell script?

Hi i am trying to comapre numbers for the biggest among double type of numbers. For example: 0.254, 0.255, 0.564, 0.984, 0.556, 0.6566, and 0.5666.

I'm using the following script code:

if [ $max -lt $i ]
if [ "$max -lt "$i" ]

But they all give the error message "integer expression expected". Help!


Dave's Answer:

The shell is doing exactly what it should be doing, believe it or not. Shell scripts can't really do proper mathematics, just piddly little simple integer math. So when you give it a number like 0.245 it sees "0" and some gobbledygook that it can't figure out, hence the error message.

There are two ways you can solve this. The first is to actually use some sort of more sophisticated mathematical tool like bc to push out the conditional expression, then test its return value to see if your condition was met or not. That's a bit tricky, particularly since bc can be difficult to use.

Instead, if indeed your input is always 0.something then I suggest that you can exploit that and simply strip off the "0." prefix, then compare them as integers and get exactly the result you seek.

Here's how I'd solve that:

#!/bin/sh

max=0
while read value
do
  val="$(echo $value | sed 's/0.//')"
  if [ $val -gt $max ]
  then
    max=$val
  fi
done

echo "max value is 0.$max"

exit 0

That should do what you seek as long as your input is always well-formed. Really, if this is going to be something that gets a lot of use, I'd probably also add some code that checks to ensure that the inputs all start with "0." to be sure it never breaks too.



Help others find this article at Del.icio.us, Digg, Netscape, Reddit, and Simpy.

Subscribe!

Never miss another useful Q&A article again! Subscribe to AskDaveTaylor with Google Reader.

Comments

If you simply strip off the "0.", then "0.984" will appear to be a smaller integer than "0.6566", but if you compare the originals as strings (dictionary order), then the result would be correct. Unfortunately, neither of these approaches works when you have different numbers of digits before and after the decimal point. Then you'd have to "normalize" by adding leading or trailing zeroes depending on whether you want to use string comparison or integer comparison.

Posted by: Paul Kosinski at January 21, 2007 9:23 PM

(sound of me slapping my head)

Doh! You're absolutely right, Paul. I'll dig into this and come up with a different solution, just for completion. :-)

Posted by: Dave Taylor at January 21, 2007 10:23 PM

This question touches on a limitation of the Bash shell--namely, that it does not understand floating point arithmetic and treats such numbers as strings.

Bash documentation even goes on to say this:

"When not to use shell scripts
...
* Procedures involving heavy-duty math operations, especially floating point arithmetic, arbitrary precision calculations, or complex numbers (use C++ or FORTRAN instead)"

So the work-arounds are indirect and perhaps (for some) counter-intuitive. One simple way to fix your code in place is to pipe your input (presumably shell variables) to bc(1), an arbitrary precision calculator language.

An example follows below:

if [ $max -lt $i ]

becomes:

if [ $(echo "$max < $i"|bc) -eq 1 ]

Where you're using a temporary subshell invocation to calculate and return a value for the test brackets. The comparison against 1 ("-eq 1") is necessary.

This is the 'quickest' and smallest solution I can see to your problem, but I'm sure there are (better?) alternative methods. Hope that helps.

Posted by: Justin K. at February 5, 2007 11:43 AM

Hello. This might make a good interview question for a toolsmith SysAdmin. Here's a script I wrote in answer. It includes flt and fgt functions, that use sort -n, as well as a test harness. Seems to work okay on this LINUX box:

#!/bin/sh
#
# "Floating Point" less-than or greater-than shell functions.
#
# Written as "proof-of-concept" answer for
# http://www.askdavetaylor.com/compare_float_real_numbers_in_a_shell_script.html
#
# Copyright 2007 Danny Howard, anyone can publish or reuse this code
# with attribution.
#
# Danny Howard
# http://dannyman.toldme.com/

function fgt () {
LHS=$1
RHS=$2

min=`(echo $LHS ; echo $RHS) | sort -n | head -1`
if [ "$min" = "$LHS" ]; then
return 1
else
return 0
fi
}

function flt () {
LHS=$1
RHS=$2

min=`(echo $LHS ; echo $RHS) | sort -n | head -1`
if [ "$min" = "$LHS" ]; then
return 0
else
return 1
fi
}
function true_or_false () {
FUNC=$1
ARG1=$2
ARG2=$3

echo -n "$FUNC $ARG1 $ARG2 ... "
$FUNC $ARG1 $ARG2

if [ $? -eq 0 ]; then
echo TRUE
else
echo FALSE
fi
}

true_or_false fgt 1.2 3.4
true_or_false flt 1.2 3.4
true_or_false fgt 0.9997 0.9996


When I run it:

> ./fgt.sh
fgt 1.2 3.4 ... FALSE
flt 1.2 3.4 ... TRUE
fgt 0.9997 0.9996 ... TRUE

Dave perhaps you can re-insert the PRE tags. ;)

Cheers,
-danny

Posted by: Danny Howard at February 14, 2007 2:36 PM

The one posted by Justin K is really a quick way of doing that, it helped me. Thanks Justin.

Posted by: Jadu at February 26, 2007 5:10 AM

use simply awk,like:
a=1.2
b=2.3
echo $a|awk '{ if ($1 < '$b') {print "a is bigger then b"}}'

Posted by: Tamas Darvas at March 14, 2007 6:29 AM

a=1.2
b=2.3
echo $a|awk '{ if ($1 > '$b') {print "a is bigger then b"}}'

Posted by: Tamas Darvas at March 14, 2007 6:43 AM

The korn shell ( open source from at&t research ) has had
floating point arithmetic since 1993. So the original test
would become:

if (( max < i )); then ... ; fi

On the other hand, I would not use 'i' for a floating point
variable, as single character variables like 'i' .. 'n' are by
convention used for integers in mathematics.

Plug: ksh93 gets shipped with some versions of unix and linux. Source and binaries can be found at at&t via kornshell.com.

Henk

Posted by: Henk Langeveld at March 18, 2007 12:26 PM

sir,
i have question to ask...
how do we compare the decimal values with the integer values in shell programming?????

and

how to find the maximum values among the no.s????

please give the answer with one or two examples.

Posted by: abhay vikram singh at July 4, 2007 6:34 AM

We can use tr+sort+head or tail to get high and low values

#To get Highest value
echo 100 500 300 400 101 204 499|tr ' ' '\n'|sort|head -n1

#To get Lowest value
echo 100 500 300 400 101 204 499|tr ' ' '\n'|sort|head -n1

#To see for yourself
echo 100 500 300 400 101 204 499|tr ' ' '\n'|sort

Posted by: Rizwaan at July 20, 2007 1:30 AM

sorry for the above typo: please use this for Lowest value:

#To get Lowest value
echo 100 500 300 400 101 204 499|tr ' ' '\n'|sort|tail -n1

Posted by: Rizwaan at July 20, 2007 1:37 AM

How do I make complex floating operation in a linux script. If I try "100 / 500 | bc" it returns 0, instead of 0.20.

Posted by: Shashank at July 24, 2007 12:27 AM

Right, Shashank. You need to be a bit more tricky because the default setting for "bc" is to have zero digits after the decimal point. What you need to do is feed the statement "scale=2" or similar to get any precision at all. I explain it -- with lots of examples -- in my book Wicked Cool Shell Scripts, which you can learn about at http://www.intuitive.com/wicked/

Posted by: Dave Taylor at July 24, 2007 6:04 AM

Thanks Justin, that short cut across comparing floating point numbers really helped me... doing that was such a torture.... however just for info is there an alternative way of doing it also ... ?

Posted by: Samreen at August 28, 2007 10:09 AM

I have a lot to say, but ...
Starbucks coffee cup I have a lot to say, and questions of my own for that matter, but most of all I'd like to say thank you for all your efforts on this Web site by buying you a chai!

I do have a comment, now that you mention it!









Remember personal info?


Please note that I will never send you any unsolicited commercial email. Ever.

While I'm at it, please note that by submitting a question or comment you're agreeing to my terms of service, which are: you relinquish any subsequent rights of ownership to your material by submitting it on this site.









Search
Find just the answers you seek from among our 1700+ free tech support articles by using our Lijit search engine.


Help!





Subscribe to
Ask Dave Taylor!

Add to Google Reader
Add to My Yahoo!
Subscribe in NewsGator Online

RDF   XML

Free Updates!
Sign up and get free weekly updates and special offers on books, seminars, workshops and more.


Recent Entries
Join the List!
Join my author info mailing list, where you'll learn about my upcoming books, speaking gigs, and more!


Book Links
© 2002 - 2008 by Dave Taylor. All Rights Reserved.

Note: This web site is for the purpose of disseminating information for educational purposes, free of charge, for the benefit of all visitors. We take great care to provide quality information. However, we do not guarantee, and accept no legal liability whatsoever arising from or connected to, the accuracy, reliability, currency or completeness of any material contained on this web site or on any linked site.

[whiteboard marker tray]