Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I want to compare two floating point numbers in a shell script. The following code is not working:

#!/bin/bash   
min=12.45
val=10.35    
if (( $val < $min )) ; then    
  min=$val
fi
echo $min 
share|improve this question
add comment

5 Answers

up vote 3 down vote accepted

You could check separately the integer and fractional parts:

#!/bin/bash
min=12.45
val=12.35    
if (( ${val%%.*} < ${min%%.*} || ( ${val%%.*} == ${min%%.*} && ${val##*.} < ${min##*.} ) )) ; then    
    min=$val
fi
echo $min

As fered says in the comments, it works only if both numbers have fractional parts. Here's a version that works for integer or fractional and any bash operator:

#!/bin/bash
shopt -s extglob
fcomp() {
    local oldIFS="$IFS" op=$2 x y digitx digity
    IFS='.' x=( ${1##+([0]|[-]|[+])}) y=( ${3##+([0]|[-]|[+])}) IFS="$oldIFS"
    while [[ "${x[1]}${y[1]}" =~ [^0] ]]; do
        digitx=${x[1]:0:1} digity=${y[1]:0:1}
        (( x[0] = x[0] * 10 + ${digitx:-0} , y[0] = y[0] * 10 + ${digity:-0} ))
        x[1]=${x[1]:1} y[1]=${y[1]:1} 
    done
    [[ ${1:0:1} == '-' ]] && (( x[0] *= -1 ))
    [[ ${3:0:1} == '-' ]] && (( y[0] *= -1 ))
    (( ${x:-0} $op ${y:-0} ))
}

for op in '==' '!=' '>' '<' '<=' '>='; do
    fcomp $1 $op $2 && echo "$1 $op $2"
done
share|improve this answer
    
+1 because it works and is simple (moreso than mine), but it will fail if either number is an integer... and just a minor note: It doesn't need the double versions (##, %%) ... # and % are enough as there won't ever be more than one '.' –  Peter.O Nov 16 '11 at 18:38
    
This can't be fixed without a lot of work (try comparing 0.5 and 0.06). You'd better use a tool that already understands the decimal notation. –  Gilles Nov 16 '11 at 23:47
    
Thanks Gilles, updated it to work more generally than the earlier version. –  ata Nov 17 '11 at 5:33
add comment

Just use ksh (ksh93 precisely), which supports floating point arithmetics natively:

$ cat test.ksh
#!/bin/ksh 
min=12.45
val=10.35    
if (( $val < $min )) ; then    
  min=$val
fi
echo $min
$ ./test.ksh
10.35

Edit: Sorry, I missed ksh93 was already suggested. Keeping my answer just to make clear the script posted in the opening question can be used with no change outside the shell switch.

share|improve this answer
add comment

For simple calculations on floating point numbers (+-*/ and comparisons), you can use awk.

min=$(echo 12.45 10.35 | awk '{if ($1 < $2) print $1; else print $2}')

Or, if you have ksh93 or zsh (not bash), you can use your shell's built-in arithmetic, which supports floating point numbers.

if ((min>val)); then ((val=min)); fi

For more advanced floating point calculations, look up bc. It actually works on arbitrary-precision fixpoint numbers.

To work on tables of numbers, look up R (example).

share|improve this answer
add comment

You can use package num-utils for simple manipulations...

For more serious maths, see this link... It describes several options, eg.

  • R / Rscript (GNU R statistical computation and graphics system)
  • octave (mostly Matlab compatible)
  • bc (The GNU bc arbitrary precision calculator language)

An example of numprocess

echo "123.456" | numprocess /+33.267,%2.33777/
# 67.0395291239087  

A programs for dealing with numbers from the command line

The 'num-utils' are a set of programs for dealing with numbers from the
Unix command line. Much like the other Unix command line utilities like
grep, awk, sort, cut, etc. these utilities work on data from both
standard in and data from files.

Includes these programs:
 * numaverage: A program for calculating the average of numbers.
 * numbound: Finds the boundary numbers (min and max) of input.
 * numinterval: Shows the numeric intervals between each number in a sequence.
 * numnormalize: Normalizes a set of numbers between 0 and 1 by default.
 * numgrep: Like normal grep, but for sets of numbers.
 * numprocess: Do mathematical operations on numbers.
 * numsum: Add up all the numbers.
 * numrandom: Generate a random number from a given expression.
 * numrange: Generate a set of numbers in a range expression.
 * numround: Round each number according to its value.

Here is a bash hack...It adds leading 0's to the integer to make a string left-to-right comparison meaningful. This particular piece of code requires that both min and val actually have a decimal point and at least one decimal digit.

min=12.45
val=10.35

MIN=0; VAL=1 # named array indexes, for clarity
IFS=.; tmp=($min $val); unset IFS 
tmp=($(printf -- "%09d.%s\n" ${tmp[@]}))
[[ ${tmp[VAL]} < ${tmp[MIN]} ]] && min=$val
echo min=$min

output:

min=10.35
share|improve this answer
add comment

Bash does not understand floating point arithmetic. It treats numbers containing a decimal point as strings.

Use awk or bc instead.

#!/bin/bash

min=12.45
val=10.35

if [ 1 -eq `echo "${val} < ${min}" | bc` ]
then  
    min=${val}
fi    

echo $min

If you intend to do a lot of math operations, it's probably better to rely on python or perl.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.