1

I'm having a function in bash using awk for certain processing and calculations and found that comparing the result of subtraction of 2 variables containing numbers does not always work. It seems like AWK for some cases leave some very small number left after subtraction of two variables (in the size e-15)

I made one example code to demonstrate this (compare.sh),

#!/bin/bash

run_compare() {
  awk ' BEGIN {

    a = 4.5
    #a = 3.75
    b = 0.15

    match_a = 3

    # // print start values and heading //
    printf ("\nStart value: %s\n", a)
    printf ("Substract value: %s\n", b)
    printf ("\n\033[4m  A:     B:       C:   is C <= %+s?\033[0m\n", match_a)

    # // Loop and substract A with B //
    for(i=0;i<26;i++) {
      printf ("%+4s - %+4s = ", a, b)
      a = a - b
      printf ("%+6s", a)
      if ( a <= match_a ) {
        printf ("          YES\n")
      } else {
        printf ("           NO\n")
      }

    }

  }'
}

run_compare

The output of this script will be,

$ ./compare.sh

Start value: 4.5
Substract value: 0.15

  A:     B:       C:   is C <= 3?
 4.5 - 0.15 =   4.35           NO
4.35 - 0.15 =    4.2           NO
 4.2 - 0.15 =   4.05           NO
4.05 - 0.15 =    3.9           NO
 3.9 - 0.15 =   3.75           NO
3.75 - 0.15 =    3.6           NO
 3.6 - 0.15 =   3.45           NO
3.45 - 0.15 =    3.3           NO
 3.3 - 0.15 =   3.15           NO
3.15 - 0.15 =      3          YES
   3 - 0.15 =   2.85          YES
2.85 - 0.15 =    2.7          YES
 2.7 - 0.15 =   2.55          YES
2.55 - 0.15 =    2.4          YES
 2.4 - 0.15 =   2.25          YES
2.25 - 0.15 =    2.1          YES
 2.1 - 0.15 =   1.95          YES
1.95 - 0.15 =    1.8          YES
 1.8 - 0.15 =   1.65          YES
1.65 - 0.15 =    1.5          YES
 1.5 - 0.15 =   1.35          YES
1.35 - 0.15 =    1.2          YES
 1.2 - 0.15 =   1.05          YES
1.05 - 0.15 =    0.9          YES
 0.9 - 0.15 =   0.75          YES
0.75 - 0.15 =    0.6          YES

This is all fine BUT, if I change to start value (a) to example 3.75 instead of 4.5 then something weird is happening,

$ ./compare.sh

Start value: 3.75
Substract value: 0.15

  A:     B:       C:   is C <= 3?
3.75 - 0.15 =    3.6           NO
 3.6 - 0.15 =   3.45           NO
3.45 - 0.15 =    3.3           NO
 3.3 - 0.15 =   3.15           NO
3.15 - 0.15 =      3           NO     # // Incorrect: This should be YES //
   3 - 0.15 =   2.85          YES
2.85 - 0.15 =    2.7          YES
 2.7 - 0.15 =   2.55          YES
2.55 - 0.15 =    2.4          YES
 2.4 - 0.15 =   2.25          YES
2.25 - 0.15 =    2.1          YES
 2.1 - 0.15 =   1.95          YES
1.95 - 0.15 =    1.8          YES
 1.8 - 0.15 =   1.65          YES
1.65 - 0.15 =    1.5          YES
 1.5 - 0.15 =   1.35          YES
1.35 - 0.15 =    1.2          YES
 1.2 - 0.15 =   1.05          YES
1.05 - 0.15 =    0.9          YES
 0.9 - 0.15 =   0.75          YES
0.75 - 0.15 =    0.6          YES
 0.6 - 0.15 =   0.45          YES
0.45 - 0.15 =    0.3          YES
 0.3 - 0.15 =   0.15          YES
0.15 - 0.15 = 1.4988e-15      YES     # // Incorrect: Should be 0 //
1.4988e-15 - 0.15 =  -0.15    YES

Is there anyone who knows why this is happening and if there is a workaround for it or if I did anything wrong? I tried to search but did not find anything explaining this particular or similar case.

BR/Ola

  • 4
    [What Every Programmer Should Know About Floating-Point Arithmetic](http://floating-point-gui.de/) – choroba Oct 01 '17 at 12:28

1 Answers1

0

Use integer instead of floating point calculations:

....
a = int(a * 100)
b = int(b * 100)
match_a = int(match_a * 100)

# // Loop and substract A with B //
for(i=0;i<26;i++) {
  printf ("%+4s - %+4s = ", a/100, b/100)
  a = a - b
  printf ("%+6s", a/100)
  if ( a <= match_a ) {
    printf ("          YES\n")
  } else {
    printf ("           NO\n")
  }

}
....

I left the +s in your printf statements so as not to muddy the parts that NEED to change but you can get rid of those as they're doing nothing.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185