38

I want to divide two numbers in awk, using integer division, i.e truncating the result. For example

k = 3 / 2
print k

should print 1

According to the manual,

Division; because all numbers in awk are floating-point numbers, the result is not rounded to an integer

Is there any workaround to get an integer value?

The reason is that I want to get the middle element of an array with integer indexes [0 to num-1].

user000001
  • 32,226
  • 12
  • 81
  • 108

3 Answers3

58

Use the int function to get the integer part of the result, truncated toward 0. This produces the nearest integer to the result, located between the result and 0. For example, int(3/2) is 1, int(-3/2) is -1.

Source: The AWK Manual - Numeric Functions

David Parks
  • 30,789
  • 47
  • 185
  • 328
Gilles Quénot
  • 173,512
  • 41
  • 224
  • 223
  • This doesn't count as integer division, integer division has a strict guarantee based on remainder. This is just "truncation" – Alec Teal Nov 10 '19 at 00:06
15

In simple cases, you can safely use int() which truncates towards zero1:

awk 'BEGIN { print int(3 / 2) }'    # prints 1
gawk 'BEGIN { print int(-3 / 2) }'  # prints -1; not guaranteed in POSIX awk

Keep in mind that awk always uses double-precision floating point numbers2 and floating-point arithmetic3, though. The only way you can get integers and integer arithmetic is to use external tools, e.g. the standard expr utility:

awk 'BEGIN { "expr 3 / 2" | getline result; print result; }'    # prints 1

This is really awkward, long, slow, … but safe and portable.


1 In POSIX awk, truncation to zero is guaranteed only for positive arguments: int(x) — Return the argument truncated to an integer. Truncation shall be toward 0 when x>0. GNU awk (gawk) uses truncation toward zero even for negative numbers: int(x) — Return the nearest integer to x, located between x and zero and truncated toward zero. For example, int(3) is 3, int(3.9) is 3, int(-3.9) is -3, and int(-3) is -3 as well.
2 Numeric expressions are specified as double-precision floats in Expressions in awk in POSIX.
3 All arithmetic shall follow the semantics of floating-point arithmetic as specified by the ISO C standard (see Concepts Derived from the ISO C Standard).POSIX awk: Arithmetic functions


If you choose to use floats, you should know about their quirks and be ready to spot them and avoid related bugs. Several scary examples:

  • Unrepresentable numbers:

    awk 'BEGIN { x = 0.875; y = 0.425; printf("%0.17g, %0.17g\n", x, y) }'
    # prints 0.875, 0.42499999999999999
    
  • Round-off errors accumulation:

    awk 'BEGIN{s=0; for(i=1;i<=100000;i++)s+=0.3; printf("%.10f, %d\n",s,int(s))}'
    # prints 29999.9999999506, 29999
    
  • Round-off errors ruin comparisons:

    awk 'BEGIN { print (0.1 + 12.2 == 12.3) }'    # prints 0
    
  • Precision decreases with magnitude, causing infinite loops:

    awk 'BEGIN { for (i=10^16; i<10^16+5; i++) printf("%d\n", i) }'
    # prints 10000000000000000 infinitely many times
    

Read more on how floats work:

  1. Stack Overflow tags wiki

  2. Wikipedia article Floating point

  3. GNU awk arbitrary precision arithmetic – contains both info on the specific implementation and general knowledge

Palec
  • 12,743
  • 8
  • 69
  • 138
  • I guess a way to overcome the floating errors (for relatively small numbers) would be to do `int(3/2+0.25)`. – user000001 Mar 25 '14 at 08:32
  • @user000001 Adding a constant does not solve the problem, it actually adds a new one. `awk 'BEGIN {print int(7/8), int (7/8 + 0.25)}'` produces `0 1`. – Palec Mar 25 '14 at 12:42
  • Yes it would have to be less than `1/(d/2)`, where `d` is the denominator. As long as this value is larger than the floating error, it should work. – user000001 Mar 25 '14 at 13:12
6

Safe and quick awk integer division can be done with:

q=(n-n%d)/d+(n<0)
MBlanc
  • 1,773
  • 15
  • 31
  • +1. Clever trick, I'll give you that. I wonder if it safe for precision errors, as @Palec explains... – user000001 Oct 05 '14 at 15:34
  • +1 This implements a `ceil` type of rounding (toward +inf). Other mathematically correct mod concepts and consequently rounding methods do exist. Some other method may be preferable. –  Nov 22 '16 at 20:17
  • @sorontar At least in my system, this is not equivalent to a `ceil` function – Marc.2377 Oct 11 '17 at 06:36
  • Hm: Using Gawk 5.1 `gawk -v n=-1 -v d=1 'BEGIN{print(n-n%d)/d+(n<0)}'` gives 0, `bash -c 'echo $((-1/1))'` of course prints -1. – xebeche Jun 21 '22 at 16:28