6

My search this morning was about how could I compare two decimal numbers in bash, and I came to this answser: How to compare to floating point number in a shell script. This one, however, doesn't include this answer here:

$ [[ ((3.56 < 2.90)) ]]; echo $?
1
$ [[ ((3.56 < 4.90)) ]]; echo $?
0

Considering that answer has been downvoted, and it looks some kind of unusual bashism, is this arithmetic evaluation trustworthy for accuracy?

10

bash does not understand floating point numbers.
Quoting bash manual page, section ARITHMETIC EVALUATION:

Evaluation is done in fixed-width integers […].

So ((3 < 4)) or ((3 < 2)) are actually correct arithmetic expressions. You can type the following:

$ echo "$((3 < 4)) -- $((3 < 2))"

output: 1 -- 0

But $ echo $((3.3 < 3.6)) will return a syntax error message. In your example, you are actually comparing strings. Hence some example:

$ [[ ((3.56 < 04.90)) ]]; echo $?

output: 1

  • 1
    s/flotting/floating/ – Josh The Geek Jun 14 '14 at 21:12
  • Oh thanks :) Bad reflex due to my native language I suppose. I'll correct it immediately. – Qeole Jun 14 '14 at 21:55
  • Both answers are awesome, but I'm choosing yours because of the example, which made me realize what could go wrong :) – Teresa e Junior Jun 15 '14 at 4:16
11

Inside [[...]] < is for string comparison.

So [[ 3.56 < 2.90 ]] or [[ (3.56 < 2.90) ]] or [[ ((3.56 < 2.90)) ]] or [[ (((3.56 < 2.90))) ]]... is just comparing the 3.56 string with the 2.90 string lexically (and lexically, 3 is greater than 10 for instance).

For integer comparison, it's [[ 3 -lt 2 ]] or (( 3 < 2 )). If you want floating point comparison, you need ksh93, zsh or yash or an external utility like awk or perl; bash can't do it.

You could for instance define a function like:

compare() (IFS=" "
  exec awk "BEGIN{if (!($*)) exit(1)}"
)

Which you could use for instance like:

if compare '1.5*10 < 1e3'; then
  echo less
fi

Or even for that matters:

if compare '"bar" < "foo"'...

to do string comparisons.

Do not pass uncontrolled externally provided data to that compare function as it would constitute a command injection vulnerability (the data is interpreted as awk code, awk can run commands with its system() for instance).

  • 2
    To illustrate the importance of the difference between string vs. numeric comparison, consider that [[ 11.56 < 2.90 ]] (and [[ ((11.56 < 2.90)) ]] and...) is true, because "1" comes before "2" in ascii sorting order. – Gordon Davisson Jun 15 '14 at 0:51
  • 1
    @GordonDavisson, yes, and [[ 0.1 < 1e-20 ]] and whether [[ -2 < 1 ]] is locale-dependent. – Stéphane Chazelas Jun 15 '14 at 9:31
  • [[ 3 -lt 2 ]] uses conditional expression, and (( 3 < 2 )) uses arithmetic expression. When comparing two integers, can these two methods always be used interchangeably? If yes, why does Bash have two methods rather than one? – Tim Apr 23 '16 at 21:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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