# Floating point comparison in shell

I am comparing the floating point values in shell script based on this reference. Following is the script contents

num1=50.960
num2=6.65E+07
echo "\${num1} < \${num2}" | bc

When I ran the script the output is '0'. But according to the comparison it should be '1'. I need inputs on why the comparison is not working as expected ?

The bc utility does not understand 6.65E+07 as the number you want it to be.

On OpenBSD, the E here is hexadecimal, so 6.65E is 6.664 (6.65 + 0.014), and then +07 will add 7 to that, yielding 13.664, and that is clearly less than 50.960. On GNU systems, 6.65E is 6.659 which is also not what you want.

Instead, you want num2 to be the string 6.65*10^7 or 66500000.

\$ num1=50.960; num2='6.65*10^7'; printf '%s < %s\n' "\$num1" "\$num2" | bc
1
• FWIW, GNU bc treats 6.65E as 6.659. If it treated 6.65E as a hex number, I'd expect it to yield 6.39794921875 (see printf '%.15g\n' 0x6.65E) Mar 16, 2021 at 11:15
• @StéphaneChazelas Ah, so this is on OpenBSD. I should mention that. Mar 16, 2021 at 11:16
• @StéphaneChazelas What I meant was that it seems to do an addition of 14 scaled to the position of the E in the number somehow. In any case, it's not doing what the user want it to. Mar 16, 2021 at 11:19
• @Kusalananda, On stackoverflow, it's suggested to use "E" for scientific notation. Are those inputs wrong ? stackoverflow.com/a/31087503/8994194
– Raja
Mar 16, 2021 at 11:28
• @Raja Yes, that is wrong. I don't know if that was correct advice at some point, but it's not correct today. Mar 16, 2021 at 11:33

If you want to use num2 as is you could use awk:

awk -v num1="\$num1" -v num2="\$num2" 'BEGIN{exit num1<=num2}'

This will compare the two numbers and exit 1 if num1 is less than num2 or exit 0 otherwise.

Note: this will exit 1 if num1 and num2 are equal which is the same behavior you would see from bc in that case, if you want it to exit 0 in that case you will need to use < instead of <=.

• Or exit num1 >= num2? Mar 16, 2021 at 11:27
• That seems the opposite of what is needed but is a better way to do it thanks Mar 16, 2021 at 11:29
• Yeah, I put may brain in backwards this morning... Mar 16, 2021 at 11:30
• I'd do awk -- 'BEGIN{exit !(ARGV[1] < ARGV[2])}' "\$num1" "\$num2" (so you can also use it as string comparison when operands are not numbers and remember -v mangles strings with backslashes). Mar 16, 2021 at 11:36
• Or compare() { awk -- "BEGIN{exit !(ARGV[1] \$2 ARGV[2])}" "\$1" "\$3"; } and then compare "\$num1" '<' "\$num2" for instance. Mar 16, 2021 at 11:41

One way is to use the dc utility by first converting the engineering notation to floating point.

num1=50.960
num2=6.65E+07

set -- "\$num1" "\$num2"

e2f() {
case \$1 in
*[eE]*) :;;
*) set -- "\$1e0"
esac
set -- "\${1//+/}"
set -- "\${1//-/  _}"
set -- "\${1//[eE]/ 10 }"
echo "\$1^*"
}

eCmp() {
test "\$(dc -e "
15k[1p]sa\$(e2f "\$1") \$(e2f "\$2")r>a")" = 1
}

## and then...
if eCmp "\$@"
then
echo "\$1 > \$2"
else
echo "\$1 <= \$2"
fi

Output:

50.960 <= 6.65E+07

Yes, bc could perform all kinds of math operations. But it doesn't understand floats. Modern shell's printf understand floats but can not do math.

Let's join them:

\$ num1=50.960
\$ num2=6.65E+07
\$ printf '%f < %f\n' "\$num1" "\$num2" | bc -l
1