script to compare two floating point numbers or expressions

script to compare two floating point numbers or expressions

Post by Roland » Sat, 06 Nov 2004 23:49:10



I wonder how other people would approach this. I often have to compare
two values when writing scripts and one or both of these values might
be an expression. I wrote a script to do it but I can't help thinking
there might be a much easier way. I am assuming the bash shell.

#!/bin/bash
# Script     : compfl
# Version    : 1.0
# Author     : Roland Rashleigh-Berry
# Date       : 03-Nov-2004
# Purpose    : To compare two floating point numbers
# SubScripts : none
# Notes      : Will work with integers and also fractions and
expressions.
#              Returns GT, EQ or LT.
#              It will calculate fractions or expressions to 9 decimal
places
# Usage      : compfl  1.2  3.4
#              compfl 355/113 3.14159
#              compfl 3*1.33  3.99
#              if [ "$(compfl 1.2 3.4)" == "LT" ] ; then
#                print "less than"
#              fi
#================================================================================
# PARAMETERS:
#-pos- -------------------------------description--------------------------------
#  1   First number (or expression) to compare
#  2   Second number (or expression) to compare
#================================================================================
# AMENDMENT HISTORY:
# init --date-- mod-id
----------------------description-------------------------
#
#================================================================================

if [ $# -ne 2 ] ; then
  echo "Usage: compfl  6.67  4.45" 1>&2
  exit 1
fi

# recalculate just in case they are fractions or expressions
num1=$(echo "scale=9; $1 + 0" | bc -l)
num2=$(echo "scale=9; $2 + 0" | bc -l)

# compare within gawk
echo | gawk '{
if (num1 > num2)
  print "GT"
else
  {
  if (num1 == num2)
    print "EQ"
  else
    print "LT"
  }

Quote:}' num1="$num1" num2="$num2"

 
 
 

script to compare two floating point numbers or expressions

Post by Stephane CHAZELA » Sun, 07 Nov 2004 00:59:59


2004-11-5, 06:49(-08), RolandRB:
Quote:> I wonder how other people would approach this. I often have to compare
> two values when writing scripts and one or both of these values might
> be an expression. I wrote a script to do it but I can't help thinking
> there might be a much easier way. I am assuming the bash shell.

[...]

given that a shell is not a tool to do mathmatics, I would
suggest you use octave, python or perl instead of a shell.

Now, some shells have floating point arithmetic support (ksh93,
zsh)

In zsh:

num1='3.14159265 * 4'
num2=13
(( num1 < num2 )) && echo "4*Pi < 13"

Or, you can use awk:

awk -vnum1="$((num1))" -vnum2="$((num2))" '
  BEGIN{exit !(num1 < num2)}' && echo "4*Pi < 13"

--
Stephane

 
 
 

script to compare two floating point numbers or expressions

Post by Chris F.A. Johnso » Sun, 07 Nov 2004 06:35:47



> I wonder how other people would approach this. I often have to compare
> two values when writing scripts and one or both of these values might
> be an expression. I wrote a script to do it but I can't help thinking
> there might be a much easier way. I am assuming the bash shell.

[snip]
> # recalculate just in case they are fractions or expressions
> num1=$(echo "scale=9; $1 + 0" | bc -l)
> num2=$(echo "scale=9; $2 + 0" | bc -l)

    Why not let bc do it all in one call?

case $( echo "scale=9; ($1) - ($2)" | bc -l ) in
    0) echo EQ ;;
    -*) echo LT ;;
    *) echo GT ;;
esac

    On the other hand, it can be done entirely with the shell. It
    looks cumbersome, but once the functions are written, they can be
    put away in a library. It doesn't handle expressions, but it
    executes 5 to 20 times faster than the bc solution. Examples of
    usage:

$ _max2fp 1.2345 1.2354
$ case $? in 0) echo EQ ;; 1) echo GT ;; 2) echo LT ;; esac
LT
$ max2fp 23.456 23.465
23.465
$ _max2fp 23.46 23.54
$ echo $?
2
$ echo $_MAX2FP
23.54
$ _max2fp 3.456 3.4560 && echo EQ || echo "NE; $_MAX2FP is larger"
EQ

    The functions:

max2fp()
{
    _max2fp $1 $2
    case $? in
        0|1) echo $1 ;;
        2) echo $2 ;;
    esac

Quote:}

_max2fp()
{
    [ $# = 2 ] || return 5
    [ "$1" = "$2" ] && return

    ## split args into integer and fractional parts
    case $1 in
        *.*) fp1=${1#*.}
             int1=${1%.*}
             dp1=${#fp1}
             ;;
        *) fp1=
           int1=$1
           dp1=0
           ;;
    esac
    case $2 in
        *.*) fp2=${2#*.}
             int2=${2%.*}
             dp2=${#fp2}
             ;;
        *) fp2=
           int2=$2
           dp2=0
           ;;
    esac

    ## if the integer parts are not equal, return result
    case $((  $int1 - $int2 )) in
        0)  ;;
        -*) return 2 ;;
        *)  return 1 ;;
    esac

    ## make lengths of fractional parts even
    while [ ${#fp1} -gt ${#fp2} ]
    do
      fp2=${fp2}0
    done
    while [ ${#fp2} -gt ${#fp1} ]
    do
      fp1=${fp1}0
    done

    ## remove leading zeroes
    while :
    do
      case $fp1 in
          0*) fp1=${fp1#0} ;;
          *) break ;;
      esac
    done
    while :
    do
      case $fp2 in
          0*) fp2=${fp2#0} ;;
          *) break ;;
      esac
    done

    ## compare the difference
    case $(( ${fp1:-0} - ${fp2:-0} )) in
        0) _MAX2FP=$1
            return 0 ;;
        -*) _MAX2FP=$2
            return 2 ;;
        *)  _MAX2FP=$1
            return 1 ;;
    esac

Quote:}

_max2()
{
    [ $# = 2 ] || { _MAX2=3; return 3; }
    case $(( $1 - $2 )) in
        0) _MAX2=$1
            return 0
            ;;
        -*) _MAX2=$2
            return 2
            ;;
        *) _MAX2=$1
           return 1
           ;;
    esac

Quote:}

--
    Chris F.A. Johnson                  http://cfaj.freeshell.org/shell
    ===================================================================
    My code (if any) in this post is copyright 2004, Chris F.A. Johnson
    and may be copied under the terms of the GNU General Public License