Industry guru Dave Taylor offers tech support on technical and business topics, including iPhone, iPod, Microsoft Windows, Sony PSP, cellphones, online advertising, CSS, Web design, business, Unix, Linux, SEO, Mac OS X, and shell script programming.     


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.


More Useful Shell Script Programming Articles:
✔   Secretly capture screenshots on my Mac?
When I used to work on a Linux system, there was a utility we had that would let me take screen captures every...
✔   Parsing "id" strings in a Shell Script?
Hello Dave. I need a Bash shell script that creates a directories with the group names automatically when user logs in to the...
✔   Copy and Paste from the Mac OS X Command Line?
I am constantly running commands in Terminal.app on my MacBook and then copying and pasting the results into email messages or documents. Yes,...
✔   Script to test line lengths for Twitter compatibility?
I've been tasked with writing a series of tweets for a Black Friday marketing campaign and am finding it a bit tricky because...
✔   Shell script to convert lowercase to title case?
As part of a project I'm working on, I find myself deep in a Linux shell script, needing to have a subroutine that...

Let's stay in touch!
Sign up for my weekly AskDaveTaylor Newsletter and you'll receive even more tech and gadget help right to your inbox, along with exclusive news and industry updates. It's good stuff. I promise!
    Enter your name: and your email addr:  





Categorized: Shell Script Programming   (Article 7141, Written by )
Tagged: scripting, shell scripts
Previous: Can I get into trouble for downloading illegal files?
Next: How do I delete Mac Dashboard widgets?




Reader Comments To Date: 20

Paul Kosinski said, on January 21, 2007 9:23 PM:

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.

Dave Taylor said, on January 21, 2007 10: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. :-)

Justin K. said, on February 5, 2007 11:43 AM:

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.

Danny Howard said, on February 14, 2007 2:36 PM:

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

Jadu said, on February 26, 2007 5:10 AM:

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

Tamas Darvas said, on March 14, 2007 6:29 AM:

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

Tamas Darvas said, on March 14, 2007 6:43 AM:

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

Henk Langeveld said, on March 18, 2007 12:26 PM:

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

abhay vikram singh said, on July 4, 2007 6:34 AM:

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.

Rizwaan said, on July 20, 2007 1:30 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

Rizwaan said, on July 20, 2007 1:37 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

Shashank said, on July 24, 2007 12:27 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.

Dave Taylor said, on July 24, 2007 6:04 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/

Samreen said, on August 28, 2007 10:09 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 ... ?

Breno BF said, on July 25, 2008 8:54 AM:

Hi, you can just do:
return_=`perl -e "if ( $cont > $cont10 ) { print 0; } else { print 1; }"`
and use $return to you next if:
if [ $return_ -eq 0 ] && [ $flag -eq 0 ]
then
bla bla bla
fi
Regards,
Breno BF

Sumanta said, on March 1, 2009 8:54 AM:

Hi
I want to divide two real numbers which will give a real no too . But even after using |bc I am getting an integer.like 5.2/1.2 is given as 4 .
how can I fix this problem??

regards
sumanta

Sebastian Castro said, on March 17, 2009 4:03 PM:

If you want to divide two real numbers using 'bc', you have to add the option '-l' (load the math library). If not, sets the precision to zero (set scale=0) and do integer operations.
if you try 'echo "5.2/1.2" | bc -l' you'll get what you expect.

Whip Hubley said, on November 2, 2009 2:25 AM:

Although the bash / bc solution would work fine, KSH is the way forward on this. Just declare your variables as floats with "typeset -F name=variable" and then continue with your calculations. It works a treat.

NYCmitch25 said, on January 5, 2010 9:13 AM:

Hey, I've just done the following using expr (found on another website):

result=`expr $val1 \> $val2`
if [ "$result" -eq "1" ]; then
echo "$val1 is larger than $val2"
exit 0
fi
result=`expr $val2 \> $val1`
if [ "$result" -eq "1" ]; then
echo "$val2 is larger than $val1"
exit 0
fi

echo "the values are equal"
exit 0


I used this to replace the integer checks -gt, -lt, etc. :

# --- calculate (T) ---
echo "${time} = ${X}"
result=`expr ${time} \= ${X}`
if [ "$result" -eq "1" ] ;then
time=`echo "scale=2 ; 60 * ${dist}" | bc`
time=`echo "scale=2 ; ${time} / ${speed}" | bc`
fi

(basically if time var IS set to the initiated arbitrary value (e.g. 9999) then it needs to have a calculation on it.

manjunath.m said, on February 17, 2011 4:40 AM:

how do we compare the max 5 number useing with the while loop in shell programming?????

Starbucks coffee cup I do have a lot to say, and questions of my own for that matter, but first I'd like to say thank you, Dave, for all your helpful information by buying you a cup of coffee!

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











I will never send you any unsolicited email. Ever.






Check This Out Too...

 
Look for Answers
Need Help? Ask Dave Taylor!


Follow Me on Pinterest

Find Me on Google+
ADT on G+
© 2002 - 2013 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. Further, 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. My lawyer says "Thanks".
"Ask Dave Taylor®" is a registered trademark of Intuitive Systems, LLC.