Related
I am trying hard to compare two floating point numbers within a Bash script. I have two 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".
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.
Caveat: Exponential notation should be written as *10^; not E, nor e.
For example:
$ echo "1*10^3==1000" |bc
1
Whereas
$ echo "1E3==1000" |bc
0
Strategies to overcome this bc limitation are discussed here.
Bash handles only integer maths, but you can use the 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 the exponent sign must be uppercase.
It's better to use AWK for noninteger 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
A 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
The 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.
It won't compare floats with integers (without dot).
You can use AWK combined with a Bash if condition:
if awk "BEGIN {exit !($d1 >= $d2)}"; then
echo "yes"
else
echo "no"
fi
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 problems with such a 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
A solution that supports all possible notations, including 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 smaller than $value2"
fi
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 [[ $((10#${num1/.})) < $((10#${num2/.})) ]]; then
...
This obviously requires you to be sure that both values have the same number of decimal places.
Please check the below edited code:
#!/bin/bash
export num1=(3.17648*e-22)
export num2=1.5
st=$((`echo "$num1 < $num2"| bc`))
if [ $st -eq 1 ]
then
echo -e "$num1 < $num2"
else
echo -e "$num1 >= $num2"
fi
This works well.
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
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
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, instead just use something readable and explicit, such as Python. Your fellow coders and future self will thank you. You can use Python code inline with Bash just like all the others.
num1=3.17648E-22
num2=1.5
if python -c "exit(0 if $num1 < $num2 else 1)"; then
echo "yes, $num1 < $num2"
else
echo "no, $num1 >= $num2"
fi
For simplicity and clarity, just use AWK for the calculations as it's a standard Unix tool and so just as likely to be present as bc and much easier to work with syntactically.
For this question:
$ cat tst.sh
#!/bin/bash
num1=3.17648e-22
num2=1.5
awk -v num1="$num1" -v num2="$num2" '
BEGIN {
print "num1", (num1 < num2 ? "<" : ">="), "num2"
}
'
$ ./tst.sh
num1 < num2
And for that other question that was closed as a duplicate of this one:
$ cat tst.sh
#!/bin/bash
read -p "Operator: " operator
read -p "First number: " ch1
read -p "Second number: " ch2
awk -v ch1="$ch1" -v ch2="$ch2" -v op="$operator" '
BEGIN {
if ( ( op == "/" ) && ( ch2 == 0 ) ) {
print "Nope..."
}
else {
print ch1 '"$operator"' ch2
}
}
'
$ ./tst.sh
Operator: /
First number: 4.5
Second number: 2
2.25
$ ./tst.sh
Operator: /
First number: 4.5
Second number: 0
Nope...
I was posting this as an answer to '4.5: syntax error: invalid arithmetic operator (error token is ".5")' - but the code still seems to work. Why? when it got closed as a duplicate of this question, so here it is as it applies here too.
This script may help where I'm checking if the installed Grails version is greater than the minimum required.
#!/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 an older version of Grails."
else
echo "Hurray, you have the latest version"
fi
Use KornShell. 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
Use this:
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
Using 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, the bashj Bash/Java hybridation offers much more...
There's one simple approach which is a bit faster than AWK and does not require bc to be installed. It leverages sort's ability to sort float numbers:
A=1280.4
B=9.325
LOW=$(sort -n <<< "$A"$'\n'"$B" | head -1)
if [[ "$LOW" == "$A" ]]; then
echo "A <= B"
else
echo "A >= B"
fi
Of course, it does not work for numbers that are equal.
Just replace the echo with a printf (it understands floats):
st=$( printf '%50G < %50G\n' "$num1" "$num2" | bc -l )
A one-liner solution
Suppose you have two variables A and B,
echo "($A > $B) * $B + ($A < $B) * $A" | bc
Here's a gawk+GMP based approach to account for a broader range of potential input :
echo " 5.65e-23 3.14e-22\n
5.65 3.14e-2203\n
3.145678 3.145679\n
3.25353E+9293 325353e9288\n
3.14159e+200000000001 3.1415899999999999999999E200000000001\n
100000 100000.0\n
4096 4096" \
\
| gawk -v PREC=9999999 -nMbe '
NF+=OFS=sprintf(" %s ",
(+($!_=sprintf("%24s",$!_)<+$NF) \
? "<" \
: (+$NF<+$!_) \
? ">" \
: (int(+$!_)==(__=int(+$NF)))*\
(__==+$NF)*index($!_,$NF ) \
? "=" \
: "\342\211\210")' | ecp
5.65e-23 < 3.14e-22
5.65 > 3.14e-2203
3.145678 < 3.145679
3.25353E+9293 ≈ 325353e9288
3.14159e+200000000001 ≈ 3.1415899999999999999999E200000000001
100000 ≈ 100000.0
4096 = 4096
For more clear-cut cases, it'll give you back a definitive answer of
less than <,
greater than >, or
exactly equal to = (purely integer cases, for now)
When it's relatively ambiguous, it outputs the Unicode character U+2248 ≈ ALMOST EQUAL TO instead of attempting to resolve it at all cost.
Most of the time you won't need PREC of 10-million; something like PREC = 32767 is good enough for most scenarios one encounters on a typical basis.
A very simple perl solution:
$ num1=3.2E8
$ num2=2.5E9
$ perl -e "print $num2 > $num1? \"true\" : \"false\", \"\n\";"
true
$ perl -e "print $num2 < $num1? \"true\" : \"false\", \"\n\";"
false
This proves that perl really understands the 'E' notation for scientific numerical representation:
$ perl -e "print $num1, \"\n\";"
320000000
If you need a 'if' statment in your shell script, use exit command in perl:
$ if perl -e "exit ($num1 > $num2? 0 : 1);"; then echo true; else echo false; fi
false
Note that in shell script, a command returning 0 is a success, and passes the 'if' condition (so the if-clause is executed). Any other non-zero return values means a failure.
I'm trying to slow down my infinite loop if CPU load exceeds certain limit, but, its just not working out right, below is the code. The if condition always results true
c=1
while [ $c -le 1 ]
do
#echo "Welcome $c times"
#php BALHABLH.php
IN=$(cat /proc/loadavg);
set -- "$IN"
IFS=" "; declare -a Array=($*)
echo "${Array[#]}"
echo "${Array[0]}"
echo "${Array[1]}"
#var = ${Array[1]}
x=$(expr "${Array[1]}" )
if [ $x > 0.91 ]
then
echo "CPU LOAD > 0.91"
sleep 2
fi
(( c++ ))
done
You need to use bc for floating point comparison and use (( ... )) for arithmetic expressions:
if (( $(bc -l <<< "$x > 0.91") == 1 ))
Also don't use cat, use:
IN=$(</proc/loadavg)
Bash cannot use floating point arithmetic. You could do something like this:
if [ $( echo "$x > 0.91" | bc ) -eq 1 ]; then
Bash only handles integers. To handle floats pipe to bc like this:
[ $(echo " $x > 0.91" | bc -l) -eq 1 ]
bc returns 1 if the comparison is true. We compare with 1 (using the -eq operator).
Validation
$ cat test.sh
#!/bin/bash
x="$1"
if [ $(echo " $x > 0.91" | bc -l) -eq 1 ]; then
echo greater;
else
echo smaller;
fi
$ ./test.sh 0.5
smaller
$ ./test.sh 1.5
greater
You can also simplify your script a bit like this:
#!/bin/bash
c=10
for (( i=1;i<=c;i++ )); do
load=$(awk '{print $2}' /proc/loadavg)
echo "$i: load is $load"
if (( $(echo "$load > 0.91" | bc) == 1 )); then
echo "CPU LOAD > 0.91"
sleep 2
fi
done
I am trying to compare two decimal values but I am getting errors.
I used
if [ "$(echo $result1 '>' $result2 | bc -l)" -eq 1 ];then
as suggested by the other Stack Overflow thread.
I am getting errors.
What is the correct way to go about this?
You can do it using Bash's numeric context:
if (( $(echo "$result1 > $result2" | bc -l) )); then
bc will output 0 or 1 and the (( )) will interpret them as false or true respectively.
The same thing using AWK:
if (( $(echo "$result1 $result2" | awk '{print ($1 > $2)}') )); then
if awk 'BEGIN{exit ARGV[1]>ARGV[2]}' "$z" "$y"
then
echo z not greater than y
else
echo z greater than y
fi
if [[ `echo "$result1 $result2" | awk '{print ($1 > $2)}'` == 1 ]]; then
echo "$result1 is greater than $result2"
fi
Following up on Dennis's reply:
Although his reply is correct for decimal points, bash throws (standard_in) 1: syntax error with floating point arithmetic.
result1=12
result2=1.27554e-05
if (( $(echo "$result1 > $result2" | bc -l) )); then
echo "r1 > r2"
else
echo "r1 < r2"
fi
This returns incorrect output with a warning although with an exit code of 0.
(standard_in) 1: syntax error
r1 < r2
While there is no clear solution to this (discussion thread 1 and thread 2), I used following partial fix by rounding off floating point results using awk followed by use of bc command as in Dennis's reply and this thread
Round off to a desired decimal place: Following will get recursive directory space in TB with rounding off at the second decimal place.
result2=$(du -s "/home/foo/videos" | tail -n1 | awk '{$1=$1/(1024^3); printf "%.2f", $1;}')
You can then use bash arithmetic as above or using [[ ]] enclosure as in following thread.
if (( $(echo "$result1 > $result2" | bc -l) )); then
echo "r1 > r2"
else
echo "r1 < r2"
fi
or using -eq operator where bc output of 1 is true and 0 is false
if [[ $(bc <<< "$result1 < $result2") -eq 1 ]]; then
echo "r1 < r2"
else
echo "r1 > r2"
fi
For shell script I couldn't use double brackets (()). So, what helped me was to split it in two rows and do the comparison in the classic way.
low_limit=4.2
value=3.9
result=$(echo "${value}<${low_limit}" | bc)
if [ $result = 1 ]; then
echo too low;
else
echo not too low;
fi
You can also echo an if...else statement to bc.
- echo $result1 '>' $result2
+ echo "if (${result1} > ${result2}) 1 else 0"
(
#export IFS=2 # example why quoting is important
result1="2.3"
result2="1.7"
if [ "$(echo $result1 '>' $result2 | bc -l)" -eq 1 ]; then echo yes; else echo no;fi
if [ "$(echo "if (${result1} > ${result2}) 1 else 0" | bc -l)" -eq 1 ];then echo yes; else echo no; fi
if echo $result1 $result2 | awk '{exit !( $1 > $2)}'; then echo yes; else echo no; fi
)
Can't bash force type conversion? For example:
($result1 + 0) < ($result2 + 0)
Why use bc ?
for i in $(seq -3 0.5 4) ; do echo $i ; if [[ (( "$i" < 2 )) ]] ; then echo "... is < 2";fi; done
The only problem : the comparison "<" doesn't work with negative numbers : they are taken as their absolute value.
I am trying hard to compare two floating point numbers within a Bash script. I have two 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".
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.
Caveat: Exponential notation should be written as *10^; not E, nor e.
For example:
$ echo "1*10^3==1000" |bc
1
Whereas
$ echo "1E3==1000" |bc
0
Strategies to overcome this bc limitation are discussed here.
Bash handles only integer maths, but you can use the 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 the exponent sign must be uppercase.
It's better to use AWK for noninteger 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
A 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
The 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.
It won't compare floats with integers (without dot).
You can use AWK combined with a Bash if condition:
if awk "BEGIN {exit !($d1 >= $d2)}"; then
echo "yes"
else
echo "no"
fi
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 problems with such a 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
A solution that supports all possible notations, including 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 smaller than $value2"
fi
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 [[ $((10#${num1/.})) < $((10#${num2/.})) ]]; then
...
This obviously requires you to be sure that both values have the same number of decimal places.
Please check the below edited code:
#!/bin/bash
export num1=(3.17648*e-22)
export num2=1.5
st=$((`echo "$num1 < $num2"| bc`))
if [ $st -eq 1 ]
then
echo -e "$num1 < $num2"
else
echo -e "$num1 >= $num2"
fi
This works well.
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
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
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, instead just use something readable and explicit, such as Python. Your fellow coders and future self will thank you. You can use Python code inline with Bash just like all the others.
num1=3.17648E-22
num2=1.5
if python -c "exit(0 if $num1 < $num2 else 1)"; then
echo "yes, $num1 < $num2"
else
echo "no, $num1 >= $num2"
fi
For simplicity and clarity, just use AWK for the calculations as it's a standard Unix tool and so just as likely to be present as bc and much easier to work with syntactically.
For this question:
$ cat tst.sh
#!/bin/bash
num1=3.17648e-22
num2=1.5
awk -v num1="$num1" -v num2="$num2" '
BEGIN {
print "num1", (num1 < num2 ? "<" : ">="), "num2"
}
'
$ ./tst.sh
num1 < num2
And for that other question that was closed as a duplicate of this one:
$ cat tst.sh
#!/bin/bash
read -p "Operator: " operator
read -p "First number: " ch1
read -p "Second number: " ch2
awk -v ch1="$ch1" -v ch2="$ch2" -v op="$operator" '
BEGIN {
if ( ( op == "/" ) && ( ch2 == 0 ) ) {
print "Nope..."
}
else {
print ch1 '"$operator"' ch2
}
}
'
$ ./tst.sh
Operator: /
First number: 4.5
Second number: 2
2.25
$ ./tst.sh
Operator: /
First number: 4.5
Second number: 0
Nope...
I was posting this as an answer to '4.5: syntax error: invalid arithmetic operator (error token is ".5")' - but the code still seems to work. Why? when it got closed as a duplicate of this question, so here it is as it applies here too.
This script may help where I'm checking if the installed Grails version is greater than the minimum required.
#!/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 an older version of Grails."
else
echo "Hurray, you have the latest version"
fi
Use KornShell. 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
Use this:
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
Using 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, the bashj Bash/Java hybridation offers much more...
There's one simple approach which is a bit faster than AWK and does not require bc to be installed. It leverages sort's ability to sort float numbers:
A=1280.4
B=9.325
LOW=$(sort -n <<< "$A"$'\n'"$B" | head -1)
if [[ "$LOW" == "$A" ]]; then
echo "A <= B"
else
echo "A >= B"
fi
Of course, it does not work for numbers that are equal.
Just replace the echo with a printf (it understands floats):
st=$( printf '%50G < %50G\n' "$num1" "$num2" | bc -l )
A one-liner solution
Suppose you have two variables A and B,
echo "($A > $B) * $B + ($A < $B) * $A" | bc
Here's a gawk+GMP based approach to account for a broader range of potential input :
echo " 5.65e-23 3.14e-22\n
5.65 3.14e-2203\n
3.145678 3.145679\n
3.25353E+9293 325353e9288\n
3.14159e+200000000001 3.1415899999999999999999E200000000001\n
100000 100000.0\n
4096 4096" \
\
| gawk -v PREC=9999999 -nMbe '
NF+=OFS=sprintf(" %s ",
(+($!_=sprintf("%24s",$!_)<+$NF) \
? "<" \
: (+$NF<+$!_) \
? ">" \
: (int(+$!_)==(__=int(+$NF)))*\
(__==+$NF)*index($!_,$NF ) \
? "=" \
: "\342\211\210")' | ecp
5.65e-23 < 3.14e-22
5.65 > 3.14e-2203
3.145678 < 3.145679
3.25353E+9293 ≈ 325353e9288
3.14159e+200000000001 ≈ 3.1415899999999999999999E200000000001
100000 ≈ 100000.0
4096 = 4096
For more clear-cut cases, it'll give you back a definitive answer of
less than <,
greater than >, or
exactly equal to = (purely integer cases, for now)
When it's relatively ambiguous, it outputs the Unicode character U+2248 ≈ ALMOST EQUAL TO instead of attempting to resolve it at all cost.
Most of the time you won't need PREC of 10-million; something like PREC = 32767 is good enough for most scenarios one encounters on a typical basis.
A very simple perl solution:
$ num1=3.2E8
$ num2=2.5E9
$ perl -e "print $num2 > $num1? \"true\" : \"false\", \"\n\";"
true
$ perl -e "print $num2 < $num1? \"true\" : \"false\", \"\n\";"
false
This proves that perl really understands the 'E' notation for scientific numerical representation:
$ perl -e "print $num1, \"\n\";"
320000000
If you need a 'if' statment in your shell script, use exit command in perl:
$ if perl -e "exit ($num1 > $num2? 0 : 1);"; then echo true; else echo false; fi
false
Note that in shell script, a command returning 0 is a success, and passes the 'if' condition (so the if-clause is executed). Any other non-zero return values means a failure.
in bash I need to compare two float numbers, one which I define in the script and the other read as paramter, for that I do:
if [[ $aff -gt 0 ]]
then
a=b
echo "xxx "$aff
#echo $CX $CY $CZ $aff
fi
but I get the error:
[[: -309.585300: syntax error: invalid arithmetic operator (error token is ".585300")
What is wrong?
Thanks
Using bc instead of awk:
float1='0.43255'
float2='0.801222'
if [[ $(echo "if (${float1} > ${float2}) 1 else 0" | bc) -eq 1 ]]; then
echo "${float1} > ${float2}"
else
echo "${float1} <= ${float2}"
fi
use awk
#!/bin/bash
num1=0.3
num2=0.2
if [ -n "$num1" -a -n "$num2" ];then
result=$(awk -vn1="$num1" -vn2="$num2" 'BEGIN{print (n1>n2)?1:0 }')
echo $result
if [ "$result" -eq 1 ];then
echo "$num1 greater than $num2"
fi
fi
Both test (which is usually linked to as [)and the bash-builtin equivalent only support integer numbers.
Use bc to check the math
a="1.21231"
b="2.22454"
c=$(echo "$a < $b" | bc)
if [ $c = '1' ]; then
echo 'a is smaller than b'
else
echo 'a is larger than b'
fi
I would use awk for that:
e=2.718281828459045
pi=3.141592653589793
if [ "yes" = "$(echo | awk "($e <= $pi) { print \"yes\"; }")" ]; then
echo "lessthanorequal"
else
echo "larger"
fi
The simplest solution is this:
f1=0.45
f2=0.33
if [[ $f1 > $f2 ]] ; then echo "f1 is greater then f2"; fi
which (on OSX) outputs:
f1 is greater then f2
Here's another example combining floating point and integer arithmetic (you need the great little perl script calc.pl that you can download from here):
dateDiff=1.9864
nObs=3
i=1
while [[ $dateDiff > 0 ]] && [ $i -le $nObs ]
do
echo "$dateDiff > 0"
dateDiff=`calc.pl $dateDiff-0.224`
i=$((i+1))
done
Which outputs
1.9864 > 0
1.7624 > 0
1.5384 > 0