1

There are some contrast output between awk arithmetic and expr.

Example

expr 11111111111111111111 / 22

gives

505050505050505050

but with awk:

echo '11111111111111111111' | awk '{q=$1/22;;print q}'

gives

505050505050505024

Can somebody explain?

1 Answers1

5

Presumably your awk works with floating point values, like GNU awk seems to do:

$ gawk 'BEGIN{ a = 11111111111111111111; print a, a/22; }'
11111111111111110656 505050505050505024

It can't store 11111111111111111111 accurately, and it can't store the remainder accurately either (11111111111111111111 / 22 is 505050505050505029.81...).

Your expr seems to have a wider numeric range. Mine doesn't (the one from GNU coreutils 8.26):

$ expr 11111111111111111111 / 22
expr: 11111111111111111111: Numerical result out of range

As @steeldriver comments, current versions of GNU awk also have the capability to use the GNU MPFR library for high-precision arithmetic. For example, quad-precision floats are enough to give an accurate answer for this division:

$ gawk -M -v PREC="quad" -v OFMT="%.6f" \
  'BEGIN{ a = 11111111111111111111; print a, a/22; }'
11111111111111111111 505050505050505050.500000

Other that that, bc or Python can be used for arbitrarily large numbers.

ilkkachu
  • 133,243
  • 15
  • 236
  • 397
  • 3
    Note that at least newer versions of GNU awk have features for [Getting the Accuracy You Need](https://www.gnu.org/software/gawk/manual/gawk.html#Getting-Accuracy) e.g. `gawk -M -v PREC="quad" -v OFMT="%.0f" 'BEGIN{ a = 11111111111111111111; print a, a/22; }'` – steeldriver Aug 17 '18 at 00:58
  • @Isaac, well, the [online manual says](https://www.gnu.org/software/gawk/manual/html_node/User_002dmodified.html#index-OFMT-variable-2) the default for `OFMT` is `%.6g`, so I get `5.05051e+17` for `gawk -M -vPREC=quad 'BEGIN{ a = 11111111111111111111; print a/22; }'`. As for just using `-M`, `gawk -M 'BEGIN{ a = 11111111111111111111; print a/22; }'` gives me the wrong answer `505050505050505024`. I'm not sure if that's a bug or if it automatically converts the number to a float when it's no longer an integer (`a/11` is an integer, and it gives the right answer with just `-M`). – ilkkachu Aug 21 '18 at 08:46
  • And there's of course `ROUNDMODE` for [setting the rounding mode](https://www.gnu.org/software/gawk/manual/html_node/Setting-the-rounding-mode.html#Setting-the-rounding-mode) if "round to even" isn't what you want – ilkkachu Aug 21 '18 at 08:50
  • @ilkkachu (1) Yes, Sorry about OFMT, yes, it is `g` not `f`. (2) If the result of a division require a decimal (some non-zero digit after the dot) then that result is internally converted to a float with the precision given by PREC. Any integer gets as many digits (before the dot) as it may need. Try `gawk -M -vPREC=20 -vOFMT='%.30f' 'BEGIN{ a=22^22; b=101; print a,a/22,b/10; }'` The first two numbers are integers and are exact, the last is as imprecise as given by `PREC=20`. Change it to `PREC=200` to see the change on the last number only.(4)Yes, rounding could be adjusted. –  Aug 21 '18 at 14:53