可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am trying hard to compare two floating point numbers within a bash script. I have to variables, e.g.
let num1=3.17648e-22
let num2=1.5
Now, I just want do a simple comparison of these two numbers:
st=`echo \"$num1 < $num2\" | bc`
if [ $st -eq 1]; then
echo -e \"$num1 < $num2\"
else
echo -e \"$num1 >= $num2\"
fi
Unfortunately, I have some problems with the right treatment of the num1 which can be of the \"e-format\". :(
Any help, hints are welcome!
回答1:
bash handles only integer maths
but you can use bc
command as follows:
$ num1=3.17648E-22
$ num2=1.5
$ echo $num1\'>\'$num2 | bc -l
0
$ echo $num2\'>\'$num1 | bc -l
1
Note that exponent sign must be uppercase
回答2:
More conveniently
This can be done more conveniently using Bash\'s numeric context:
if (( $(echo \"$num1 > $num2\" |bc -l) )); then
…
fi
Explanation
Piping through the basic calculator command bc
returns either 1 or 0.
The option -l
is equivalent to --mathlib
; it loads the standard math library.
Enclosing the whole expression between double parenthesis (( ))
will translate these values to respectively true or false.
Please, ensure that the bc
basic calculator package is installed.
回答3:
It\'s better to use awk
for non integer mathematics. You can use this bash utility function:
numCompare() {
awk -v n1=\"$1\" -v n2=\"$2\" \'BEGIN {printf \"%s \" (n1<n2?\"<\":\">=\") \" %s\\n\", n1, n2}\'
}
And call it as:
numCompare 5.65 3.14e-22
5.65 >= 3.14e-22
numCompare 5.65e-23 3.14e-22
5.65e-23 < 3.14e-22
numCompare 3.145678 3.145679
3.145678 < 3.145679
回答4:
Pure bash solution for comparing floats without exponential notation, leading or trailing zeros:
if [ ${FOO%.*} -eq ${BAR%.*} ] && [ ${FOO#*.} \\> ${BAR#*.} ] || [ ${FOO%.*} -gt ${BAR%.*} ]; then
echo \"${FOO} > ${BAR}\";
else
echo \"${FOO} <= ${BAR}\";
fi
Order of logical operators matters. Integer parts are compared as numbers and fractional parts are intentionally compared as strings. Variables are split into integer and fractional parts using this method.
Won\'t compare floats with integers (without dot).
回答5:
you can use awk combined with a bash if condition, awk will print 1 or 0 and those will be interpreted by if clause with true or false.
if (( $(awk \'BEGIN {print (\"\'$d1\'\" >= \"\'$d2\'\")}\') )); then
echo \"yes\"
else
echo \"no\"
fi
回答6:
beware when comparing numbers that are package versions, like checking if grep 2.20 is greater than version 2.6:
$ awk \'BEGIN { print (2.20 >= 2.6) ? \"YES\" : \"NO\" }\'
NO
$ awk \'BEGIN { print (2.2 >= 2.6) ? \"YES\" : \"NO\" }\'
NO
$ awk \'BEGIN { print (2.60 == 2.6) ? \"YES\" : \"NO\" }\'
YES
I solved such problem with such shell/awk function:
# get version of GNU tool
toolversion() {
local prog=\"$1\" operator=\"$2\" value=\"$3\" version
version=$($prog --version | awk \'{print $NF; exit}\')
awk -vv1=\"$version\" -vv2=\"$value\" \'BEGIN {
split(v1, a, /\\./); split(v2, b, /\\./);
if (a[1] == b[1]) {
exit (a[2] \'$operator\' b[2]) ? 0 : 1
}
else {
exit (a[1] \'$operator\' b[1]) ? 0 : 1
}
}\'
}
if toolversion grep \'>=\' 2.6; then
# do something awesome
fi
回答7:
I used the answers from here and put them in a function, you can use it like this:
is_first_floating_number_bigger 1.5 1.2
result=\"${__FUNCTION_RETURN}\"
Once called, echo $result
will be 1
in this case, otherwise 0
.
The function:
is_first_floating_number_bigger () {
number1=\"$1\"
number2=\"$2\"
[ ${number1%.*} -eq ${number2%.*} ] && [ ${number1#*.} \\> ${number2#*.} ] || [ ${number1%.*} -gt ${number2%.*} ];
result=$?
if [ \"$result\" -eq 0 ]; then result=1; else result=0; fi
__FUNCTION_RETURN=\"${result}\"
}
Or a version with debug output:
is_first_floating_number_bigger () {
number1=\"$1\"
number2=\"$2\"
echo \"... is_first_floating_number_bigger: comparing ${number1} with ${number2} (to check if the first one is bigger)\"
[ ${number1%.*} -eq ${number2%.*} ] && [ ${number1#*.} \\> ${number2#*.} ] || [ ${number1%.*} -gt ${number2%.*} ];
result=$?
if [ \"$result\" -eq 0 ]; then result=1; else result=0; fi
echo \"... is_first_floating_number_bigger: result is: ${result}\"
if [ \"$result\" -eq 0 ]; then
echo \"... is_first_floating_number_bigger: ${number1} is not bigger than ${number2}\"
else
echo \"... is_first_floating_number_bigger: ${number1} is bigger than ${number2}\"
fi
__FUNCTION_RETURN=\"${result}\"
}
Just save the function in a separated .sh
file and include it like this:
. /path/to/the/new-file.sh
回答8:
This script may help where I\'m checking if installed grails
version is greater than minimum required. Hope it helps.
#!/bin/bash
min=1.4
current=`echo $(grails --version | head -n 2 | awk \'{print $NF}\' | cut -c 1-3)`
if [ 1 -eq `echo \"${current} < ${min}\" | bc` ]
then
echo \"yo, you have older version of grails.\"
else
echo \"Hurray, you have the latest version\"
fi
回答9:
Use korn shell, in bash you may have to compare the decimal part separately
#!/bin/ksh
X=0.2
Y=0.2
echo $X
echo $Y
if [[ $X -lt $Y ]]
then
echo \"X is less than Y\"
elif [[ $X -gt $Y ]]
then
echo \"X is greater than Y\"
elif [[ $X -eq $Y ]]
then
echo \"X is equal to Y\"
fi
回答10:
Of course, if you don\'t need really floating-point arithmetic, just arithmetic on e.g. dollar values where there are always exactly two decimal digits, you might just drop the dot (effectively multiplying by 100) and compare the resulting integers.
if [[ ${num1/.} < ${num2/.} ]]; then
...
This obviously requires you to be sure that both values have the same number of decimal places.
回答11:
Using bashj (https://sourceforge.net/projects/bashj/ ), a bash mutant with java support, you just write (and it IS easy to read):
#!/usr/bin/bashj
#!java
static int doubleCompare(double a,double b) {return((a>b) ? 1 : (a<b) ? -1 : 0);}
#!bashj
num1=3.17648e-22
num2=1.5
comp=j.doubleCompare($num1,$num2)
if [ $comp == 0 ] ; then echo \"Equal\" ; fi
if [ $comp == 1 ] ; then echo \"$num1 > $num2\" ; fi
if [ $comp == -1 ] ; then echo \"$num2 > $num1\" ; fi
Of course bashj bash/java hybridation offers much more...
回答12:
How about this? =D
VAL_TO_CHECK=\"1.00001\"
if [ $(awk \'{printf($1 >= $2) ? 1 : 0}\' <<<\" $VAL_TO_CHECK 1 \") -eq 1 ] ; then
echo \"$VAL_TO_CHECK >= 1\"
else
echo \"$VAL_TO_CHECK < 1\"
fi
回答13:
awk
and tools like it (I\'m staring at you sed
...) should be relegated to the dustbin of old projects, with code that everyone is too afraid to touch since it was written in a read-never language.
Or you\'re the relatively rare project that needs to prioritize CPU usage optimization over code maintenance optimization... in which case, carry on.
If not, though, why not instead just use something readable and explicit, such as python
? Your fellow coders and future self will thank you. You can use python
inline with bash just like all the others.
num1=3.17648E-22
num2=1.5
if python -c \"import sys; sys.exit(0 if float($num1) < float($num2) else 1)\"; then
echo \"yes, $num1 < $num2\"
else
echo \"no, $num1 >= $num2\"
fi
回答14:
num1=0.555
num2=2.555
if [ `echo \"$num1>$num2\"|bc` -eq 1 ]; then
echo \"$num1 is greater then $num2\"
else
echo \"$num2 is greater then $num1\"
fi
回答15:
A solution supporting the scientific notation with both uppercase and lowercase exponents (e.g., 12.00e4
):
if (( $(bc -l <<< \"${value1/e/E} < ${value2/e/E}\") ))
then
echo \"$value1 is less than $value2\"
fi