Search code examples
error-handlingawkdivide-by-zero

Why does awk produce different results for division by zero and how can I test for this?


I get six different results when dividing by zero in mawk:

$ echo | awk '{print -1/0 }' ; echo $?
-inf
0
$ echo | awk '{print 0/0 }' ; echo $?
-nan
0
$ echo | awk '{print 1/0 }' ; echo $?
inf
0
$ echo | awk '{printf ("%i\n", -1/0) }' ; echo $?
-2147483647
0
$ echo | awk '{printf ("%i\n", 0/0) }' ; echo $?
-2147483647
0
$ echo | awk '{printf ("%i\n", 1/0) }' ; echo $?
2147483647
0

I get a "success" exit code for each case.

  1. Why does this happen?
  2. What can I do about it? Do I always have to check for zero divisors before doing a division or is there a way I can rely on awk's error handling and exit codes?

Solution

  • I'm guessing you are using mawk, none of awk, nawk or gawk have this behaviour, division by zero is not supported on those.

    Your answers are +inf for positive infinity, nan for "not a number" and -inf for negative infinity, all as expected. These are only output when printing a float, i.e. with %.6g which is the default OFMT (default format used for printing numbers).

    $ mawk 'BEGIN {printf ("%i\n", 1/0) }'
    2147483647
    $ mawk 'BEGIN {printf ("%f\n", 1/0) }'
    inf
    $ gawk 'BEGIN {printf ("%f\n", 1/0) }'
    gawk: fatal: division by zero attempted
    

    When you print explicitly with "%i" you get +HUGE or -HUGE instead, this is converted to an int (signed 32-bit in your case) and printed as + or - (2^31-1).

    The usual practice is to always check for divide by zero, ideally minimising the number of times you need to check by reorganising your expressions -- divide by zero causes other implementations of awk to simply terminate.

    When mawk is building it detects the capabilities of your C math library:

    $ ./configure
    [...]
    checking handling of floating point exceptions
        division by zero does not generate an exception
        overflow does not generate an exception
        math library supports ieee754
    

    You can get "standard" behaviour if you build with NOINFO_SIGFPE defined:

    $ ./configure CFLAGS="-DNOINFO_SIGFPE"
    $ make clean && make
    $ ./mawk 'BEGIN {printf ("%f\n", 1/0) }'
    mawk: run time error: division by zero
        FILENAME="" FNR=0 NR=0
    

    (though this isn't documented, it may not be something you should rely on).