23

I want to compare a floating point variable to an integer. I know this is not the best to do with bash, but my whole script is already written in bash. $number can be any integer. If it below or equal 50, I want output1, for all others I want an output with the other variable k. This is what I have so far:

number=43 test=$(echo "scale=2; $number/50" | bc -l) 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 

If I try with test=0.43, the first loop does not even work. I think it has to do with an integer and a floating point comparison but cannot make it work.

Anything I am missing?

PS:this [0.43: command not found is what the terminal outputs.

1
  • you can also just switch to ksh and use typeset to automatically round. (that's available since 1993, works well) Commented Sep 20, 2016 at 1:04

2 Answers 2

45

Bash can't handle floats. Pipe to bc instead:

if [ $(echo " $test > $k" | bc) -eq 1 ] 

The error you see though is because the test command (i.e. the [) needs spaces before and after

It is even better to use (( ... )) since you compare numbers like this:

if (( $(bc <<< "$test > $k") )) 

The part in the loop should look like this:

if (( $(bc <<< "$test <= 1") )) then echo "output" elif (( $(bc <<< "$test > $k") )) then echo "output$k" fi 

Relational expressions evaluate to 0, if the relation is false, and 1 if the relation is true [source]. Note however that is a behavior of GNU bc, and it is not POSIX compiant.

Sign up to request clarification or add additional context in comments.

4 Comments

Also: bash: [: 1.3: integer expression expected will be the error if you had formatted the [ ] expression correctly. +1@user000001
Great thank you! It works. Minor question, I am trying to look into. I wanted the first if to be either less or equal to 1 and the other ones strictly higher than k. Can I write (( $(bc <<< "$test < 1" && "$test = 1") == 1 )) or (( $(bc <<< "$test <= 1") == 1 ))?
The second one is correct. The first would be correct it you quoted like this: (( $(bc <<< "$test < 1 && $test == 1") == 1 )) (nore also the ==). For more info on boolean Expressions in bc see here: gnu.org/software/bc/manual/html_mono/bc.html#SEC12
Change && to || in my previous comment
10

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

1 Comment

That looks pretty good but for the sake of copy-pasters I'd set the printf format to something more sensible (floats can be pretty long, and keeping the precision low won't make this function any faster).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.