0

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 ?

2

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
5
  • 2
    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) – Stéphane Chazelas Mar 16 at 11:15
  • @StéphaneChazelas Ah, so this is on OpenBSD. I should mention that. – Kusalananda Mar 16 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. – Kusalananda Mar 16 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 at 11:28
  • 1
    @Raja Yes, that is wrong. I don't know if that was correct advice at some point, but it's not correct today. – Kusalananda Mar 16 at 11:33
0

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 <=.

5
  • 1
    Or exit num1 >= num2? – Kusalananda Mar 16 at 11:27
  • That seems the opposite of what is needed but is a better way to do it thanks – jesse_b Mar 16 at 11:29
  • Yeah, I put may brain in backwards this morning... – Kusalananda Mar 16 at 11:30
  • 1
    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). – Stéphane Chazelas Mar 16 at 11:36
  • Or compare() { awk -- "BEGIN{exit !(ARGV[1] $2 ARGV[2])}" "$1" "$3"; } and then compare "$num1" '<' "$num2" for instance. – Stéphane Chazelas Mar 16 at 11:41
0

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
0

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

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.