Skip to main content
better use of printf, fix error regarding forks.
Source Link
ghoti
  • 47.2k
  • 8
  • 71
  • 108

Kind of an old question, but it bears an additional answer I think.

While piping to a higher precision calculator (bc or dc) works, it is at the cost of a fork and a an extra process, since those calculators are not built in to bash. One thing that IS built in, though, is printf. So if you can deal with your numbers being within a particular number of decimal places, you can "fake" floating point comparisons, with a function like this:

#!/usr/bin/env bash function [[[ () { local LANG=C   lhs localrhs  lhs=$( printf -v lhs '%07.3f' "$1");"$1"; lhs=${lhs/./} local rhs=$(printf -v rhs '%07.3f' "$3");"$3"; rhs=${rhs/./} case "$2" in -lt) return $(( ! ( 10#$lhs < 10#$rhs ) )) ;; -le) return $(( ! ( 10#$lhs <= 10#$rhs ) )) ;; -eq) return $(( ! ( 10#$lhs == 10#$rhs ) )) ;; -ge) return $(( ! ( 10#$lhs >= 10#$rhs ) )) ;; -gt) return $(( ! ( 10#$lhs > 10#$rhs ) )) ;; esac } number=${1:-43} test=$(dc -e "2k $number 50 / p") echo "$test" for k in {1..5}; do if [[[ "$test" -le 1 ]]]; then echo "output" elif [[[ "$test" -gt "$k" ]]]; then echo "output $k" fi done 

A few things to consider here.

  • I've named the function [[[ to be cute. You can name it whatever you like. ntest or mynumericcomparison or even [[[.
  • printf is an internal function within bash, so despite the fact that at first glance this looks as if it will launch subshellsit's on your path, it should notdoesn't cost a fork.
  • As it stands, the function supports numbers up to 999.999. If you need higher numbers (or more precision), adjust the printf formats.
  • The 10# at the beginning of each variable inside the case statement is to force the comparison to happen at base 10, since a zero-padded number might otherwise be interpreted as octal.

See also: http://mywiki.wooledge.org/BashFAQ/022

Kind of an old question, but it bears an additional answer I think.

While piping to a higher precision calculator (bc or dc) works, it is at the cost of a fork and a an extra process, since those calculators are not built in to bash. One thing that IS built in, though, is printf. So if you can deal with your numbers being within a particular number of decimal places, you can "fake" floating point comparisons, with a function like this:

#!/usr/bin/env bash function [[[ () { local LANG=C   local lhs=$(printf '%07.3f' "$1"); lhs=${lhs/./} local rhs=$(printf '%07.3f' "$3"); rhs=${rhs/./} case "$2" in -lt) return $(( ! ( 10#$lhs < 10#$rhs ) )) ;; -le) return $(( ! ( 10#$lhs <= 10#$rhs ) )) ;; -eq) return $(( ! ( 10#$lhs == 10#$rhs ) )) ;; -ge) return $(( ! ( 10#$lhs >= 10#$rhs ) )) ;; -gt) return $(( ! ( 10#$lhs > 10#$rhs ) )) ;; esac } number=${1:-43} test=$(dc -e "2k $number 50 / p") echo "$test" for k in {1..5}; do if [[[ "$test" -le 1 ]]]; then echo "output" elif [[[ "$test" -gt "$k" ]]]; then echo "output $k" fi done 

A few things to consider here.

  • I've named the function [[[ to be cute. You can name it whatever you like. ntest or mynumericcomparison or even [[[.
  • printf is an internal function within bash, so despite the fact that at first glance this looks as if it will launch subshells, it should not.
  • As it stands, the function supports numbers up to 999.999. If you need higher numbers (or more precision), adjust the printf formats.
  • The 10# at the beginning of each variable inside the case statement is to force the comparison to happen at base 10, since a zero-padded number might otherwise be interpreted as octal.

See also: http://mywiki.wooledge.org/BashFAQ/022

Kind of an old question, but it bears an additional answer I think.

While piping to a higher precision calculator (bc or dc) works, it is at the cost of a fork and a an extra process, since those calculators are not built in to bash. One thing that IS built in, though, is printf. So if you can deal with your numbers being within a particular number of decimal places, you can "fake" floating point comparisons, with a function like this:

#!/usr/bin/env bash function [[[ () { local LANG=C lhs rhs   printf -v lhs '%07.3f' "$1"; lhs=${lhs/./} printf -v rhs '%07.3f' "$3"; rhs=${rhs/./} case "$2" in -lt) return $(( ! ( 10#$lhs < 10#$rhs ) )) ;; -le) return $(( ! ( 10#$lhs <= 10#$rhs ) )) ;; -eq) return $(( ! ( 10#$lhs == 10#$rhs ) )) ;; -ge) return $(( ! ( 10#$lhs >= 10#$rhs ) )) ;; -gt) return $(( ! ( 10#$lhs > 10#$rhs ) )) ;; esac } number=${1:-43} test=$(dc -e "2k $number 50 / p") echo "$test" for k in {1..5}; do if [[[ "$test" -le 1 ]]]; then echo "output" elif [[[ "$test" -gt "$k" ]]]; then echo "output $k" fi done 

A few things to consider here.

  • I've named the function [[[ to be cute. You can name it whatever you like. ntest or mynumericcomparison or even [[[.
  • printf is an internal function within bash, so despite the fact that it's on your path, it doesn't cost a fork.
  • As it stands, the function supports numbers up to 999.999. If you need higher numbers (or more precision), adjust the printf formats.
  • The 10# at the beginning of each variable inside the case statement is to force the comparison to happen at base 10, since a zero-padded number might otherwise be interpreted as octal.

See also: http://mywiki.wooledge.org/BashFAQ/022

fixed locale issue and inverse logic on returns (thanks @geirha)
Source Link
ghoti
  • 47.2k
  • 8
  • 71
  • 108

Kind of an old question, but it bears an additional answer I think.

While piping to a higher precision calculator (bc or dc) works, it is at the cost of a fork and a an extra process, since those calculators are not built in to bash. One thing that IS built in, though, is printf. So if you can deal with your numbers being within a particular number of decimal places, you can "fake" floating point comparisons, with a function like this:

#!/usr/bin/env bash function [[[ () { local LANG=C local lhs=$(printf '%07.3f' "$1"); lhs=${lhs/./}   local rhs=$(printf '%07.3f' "$3"); rhs=${rhs/./} case "$2" in -lt) return $(( ! ( 10#$lhs < 10#$rhs ) )) ;; -le) return $(( ! ( 10#$lhs <= 10#$rhs ) )) ;; -eq) return $(( ! ( 10#$lhs == 10#$rhs ) )) ;; -ge) return $(( ! ( 10#$lhs >= 10#$rhs ) )) ;; -gt) return $(( ! ( 10#$lhs > 10#$rhs ) )) ;; esac } number=${1:-43} test=$(dc -e "2k $number 50 / p") echo "$test" for k in {1..5}; do if [[[ "$test" -le 1 ]]]; then echo "output" elif [[[ "$test" -gt "$k" ]]]; then echo "output $k" fi done 

A few things to consider here.

  • I've named the function [[[ to be cute. You can name it whatever you like. ntest or mynumericcomparison or even [[[.
  • printf is an internal function within bash, so despite the fact that at first glance this looks as if it will launch subshells, it should not.
  • As it stands, the function supports numbers up to 999.999. If you need higher numbers (or more precision), adjust the printf formats.
  • The 10# at the beginning of each variable inside the case statement is to force the comparison to happen at base 10, since a zero-padded number might otherwise be interpreted as octal.

See also: http://mywiki.wooledge.org/BashFAQ/022

Kind of an old question, but it bears an additional answer I think.

While piping to a higher precision calculator (bc or dc) works, it is at the cost of a fork and a an extra process, since those calculators are not built in to bash. One thing that IS built in, though, is printf. So if you can deal with your numbers being within a particular number of decimal places, you can "fake" floating point comparisons, with a function like this:

#!/usr/bin/env bash function [[[ () { lhs=$(printf '%07.3f' "$1"); lhs=${lhs/./} rhs=$(printf '%07.3f' "$3"); rhs=${rhs/./} case "$2" in -lt) return $(( 10#$lhs < 10#$rhs )) ;; -le) return $(( 10#$lhs <= 10#$rhs )) ;; -eq) return $(( 10#$lhs == 10#$rhs )) ;; -ge) return $(( 10#$lhs >= 10#$rhs )) ;; -gt) return $(( 10#$lhs > 10#$rhs )) ;; esac } number=${1:-43} test=$(dc -e "2k $number 50 / p") echo "$test" for k in {1..5}; do if [[[ "$test" -le 1 ]]]; then echo "output" elif [[[ "$test" -gt "$k" ]]]; then echo "output $k" fi done 

A few things to consider here.

  • I've named the function [[[ to be cute. You can name it whatever you like. ntest or mynumericcomparison or even [[[.
  • printf is an internal function within bash, so despite the fact that at first glance this looks as if it will launch subshells, it should not.
  • As it stands, the function supports numbers up to 999.999. If you need higher numbers (or more precision), adjust the printf formats.
  • The 10# at the beginning of each variable inside the case statement is to force the comparison to happen at base 10, since a zero-padded number might otherwise be interpreted as octal.

See also: http://mywiki.wooledge.org/BashFAQ/022

Kind of an old question, but it bears an additional answer I think.

While piping to a higher precision calculator (bc or dc) works, it is at the cost of a fork and a an extra process, since those calculators are not built in to bash. One thing that IS built in, though, is printf. So if you can deal with your numbers being within a particular number of decimal places, you can "fake" floating point comparisons, with a function like this:

#!/usr/bin/env bash function [[[ () { local LANG=C local lhs=$(printf '%07.3f' "$1"); lhs=${lhs/./}   local rhs=$(printf '%07.3f' "$3"); rhs=${rhs/./} case "$2" in -lt) return $(( ! ( 10#$lhs < 10#$rhs ) )) ;; -le) return $(( ! ( 10#$lhs <= 10#$rhs ) )) ;; -eq) return $(( ! ( 10#$lhs == 10#$rhs ) )) ;; -ge) return $(( ! ( 10#$lhs >= 10#$rhs ) )) ;; -gt) return $(( ! ( 10#$lhs > 10#$rhs ) )) ;; esac } number=${1:-43} test=$(dc -e "2k $number 50 / p") echo "$test" for k in {1..5}; do if [[[ "$test" -le 1 ]]]; then echo "output" elif [[[ "$test" -gt "$k" ]]]; then echo "output $k" fi done 

A few things to consider here.

  • I've named the function [[[ to be cute. You can name it whatever you like. ntest or mynumericcomparison or even [[[.
  • printf is an internal function within bash, so despite the fact that at first glance this looks as if it will launch subshells, it should not.
  • As it stands, the function supports numbers up to 999.999. If you need higher numbers (or more precision), adjust the printf formats.
  • The 10# at the beginning of each variable inside the case statement is to force the comparison to happen at base 10, since a zero-padded number might otherwise be interpreted as octal.

See also: http://mywiki.wooledge.org/BashFAQ/022

Source Link
ghoti
  • 47.2k
  • 8
  • 71
  • 108

Kind of an old question, but it bears an additional answer I think.

While piping to a higher precision calculator (bc or dc) works, it is at the cost of a fork and a an extra process, since those calculators are not built in to bash. One thing that IS built in, though, is printf. So if you can deal with your numbers being within a particular number of decimal places, you can "fake" floating point comparisons, with a function like this:

#!/usr/bin/env bash function [[[ () { lhs=$(printf '%07.3f' "$1"); lhs=${lhs/./} rhs=$(printf '%07.3f' "$3"); rhs=${rhs/./} case "$2" in -lt) return $(( 10#$lhs < 10#$rhs )) ;; -le) return $(( 10#$lhs <= 10#$rhs )) ;; -eq) return $(( 10#$lhs == 10#$rhs )) ;; -ge) return $(( 10#$lhs >= 10#$rhs )) ;; -gt) return $(( 10#$lhs > 10#$rhs )) ;; esac } number=${1:-43} test=$(dc -e "2k $number 50 / p") echo "$test" for k in {1..5}; do if [[[ "$test" -le 1 ]]]; then echo "output" elif [[[ "$test" -gt "$k" ]]]; then echo "output $k" fi done 

A few things to consider here.

  • I've named the function [[[ to be cute. You can name it whatever you like. ntest or mynumericcomparison or even [[[.
  • printf is an internal function within bash, so despite the fact that at first glance this looks as if it will launch subshells, it should not.
  • As it stands, the function supports numbers up to 999.999. If you need higher numbers (or more precision), adjust the printf formats.
  • The 10# at the beginning of each variable inside the case statement is to force the comparison to happen at base 10, since a zero-padded number might otherwise be interpreted as octal.

See also: http://mywiki.wooledge.org/BashFAQ/022