9

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?

admirabilis
  • 4,712

2 Answers2

13

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

Qeole
  • 694
  • Both answers are awesome, but I'm choosing yours because of the example, which made me realize what could go wrong :) – admirabilis Jun 15 '14 at 04:16
13

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 00:51
  • 1
    @GordonDavisson, yes, and [[ 0.1 < 1e-20 ]] and whether [[ -2 < 1 ]] is locale-dependent. – Stéphane Chazelas Jun 15 '14 at 09: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