awk Part II · Module 5 · Variables, Operators, and Control Flow
Arithmetic and String Expressions
Chapter 5.1 used += and ++ to build totals without examining either. This chapter examines them: the full operator set, precedence and the two associativity surprises, the difference between i++ and ++i, and what awk does when you divide by zero. Then the part that matters most in engineering work — awk will convert any string to a number without a word of complaint, so a single n/a in a measurement column produces a report whose total is exactly right and whose average is wrong by a third. Finding that is easy. Guarding against it correctly takes one more idea than most people reach for.
Foundation19 min readawkPart IIArithmeticOperatorsData Quality
Part II · Module 5 · Chapter 5.3 · Arithmetic and String Expressions
1. Why This Matters
Every number in an engineering report is computed by something. If that something is awk, this chapter is the list of ways it can be quietly wrong.
The operators themselves are the easy half — they are the ones you already know from C, with two associativity surprises and one modulo wrinkle. Sections 2 through 8 cover them with output.
The hard half is sections 9 through 11, and it is specific to awk's greatest convenience: awk converts a string to a number whenever arithmetic is asked for, and never reports a failure. "n/a" + 0 is 0. No warning, no non-zero exit, nothing in a log. That single design decision is why awk one-liners are short, and it is why a report containing one malformed cell can produce a number that passes every sanity check a reviewer applies.
2. The Arithmetic Operators
Six of them:
awk 'BEGIN { print 7+2, 7-2, 7*2, 7/2, 7%2, 7^2 }'9 5 14 3.5 1 49| Operator | Meaning | 7 ? 2 |
|---|---|---|
+ | addition | 9 |
- | subtraction | 5 |
* | multiplication | 14 |
/ | division | 3.5 |
% | modulo (remainder) | 1 |
^ | exponentiation | 49 |
Note 7/2 is 3.5, not 3. awk has one numeric type and it is floating point — there is no integer division operator and no automatic truncation. If you want 3, you ask for it explicitly with int(7/2), which is Module 8's territory.
That is a genuine difference from C and from most shells, and it cuts both ways. $2/$3 giving a fraction is usually what an engineer wants. A loop index computed by division is usually not.
^ is exponentiation. Some awks also accept ** as a synonym; ^ is the portable spelling and the one to write.
3. Precedence and Associativity
Precedence follows normal arithmetic convention — multiplication and division before addition and subtraction:
awk 'BEGIN { print 10 + 5 / 3; print (10 + 5) / 3 }'11.6667
5Division first in the left case, so 10 + 1.6667. Parentheses override it, as always. (11.6667 rather than 11.666666… is OFMT at work — Chapter 5.2, section 12.)
Two cases do not follow intuition and both are worth memorising:
awk 'BEGIN { print 2 ^ 3 ^ 2 }'512^ is right-associative. This is 2 ^ (3 ^ 2) = 2 ^ 9 = 512, not (2 ^ 3) ^ 2 = 64. Every other arithmetic operator in awk groups left to right; exponentiation groups right to left, matching mathematical convention rather than the pattern set by its neighbours.
awk 'BEGIN { print -2 ^ 2 }'-4^ binds tighter than unary minus. This is -(2 ^ 2) = -4, not (-2) ^ 2 = 4. If you want the square of negative two, the parentheses are mandatory:
awk 'BEGIN { print (-2) ^ 2 }'4The full precedence order, loosest to tightest, for the operators covered so far:
| Precedence | Operators |
|---|---|
| loosest | = += -= *= /= %= ^= |
?: (conditional) | |
| ` | |
< <= > >= == != | |
| (string concatenation — section 12) | |
+ - | |
* / % | |
unary + - | |
| tightest | ^ ++ -- |
The row worth staring at is concatenation, which sits looser than + and -. Section 12 shows why that is mostly harmless and exactly when it is not.
4. Modulo, Including Negatives
% gives the remainder. Its behaviour with negative operands is the part that trips people:
awk 'BEGIN { print -7 % 2, 7 % -2, -7 % -2 }'-1 1 -1The sign of the result follows the dividend — the left operand — not the divisor. -7 % 2 is -1 because -7 is negative. 7 % -2 is 1 because 7 is positive.
This matches C and most compiled languages, and it differs from Python, where -7 % 2 is 1. If you are moving a script between the two, this is a real source of off-by-sign bugs.
The common engineering use of % is sampling every Nth record, where the operands are positive and the wrinkle never appears:
u_alu 1820
u_regfile 2410
u_io 640awk 'NR % 2 == 1 { print NR, $1 }' area.rpt1 u_alu
3 u_ioEvery odd record. On a million-line timing report, NR % 1000 == 0 is how you get a feel for the data without reading it all.
5. Increment and Decrement
++ adds one and -- subtracts one. Each has a prefix and a postfix form, and they differ in what the expression evaluates to:
awk 'BEGIN { i = 5; print i++, i }'5 6awk 'BEGIN { j = 5; print ++j, j }'6 6i++ is post-increment: it yields the old value (5) and then increments, so by the time the second item prints, i is 6.
++j is pre-increment: it increments first and yields the new value, so both items print 6.
When the value is not used — which is almost always, in a counter — the two are identical:
awk '$2 == "FAIL" { f++ } END { print f + 0 }' results.txtalu PASS
fifo FAIL
uart PASS
spi FAIL2f++ and ++f both produce 2 here, because the statement throws the expression's value away. Chapter 5.1's counters used ++ in exactly this position, which is why the distinction never came up.
The practical guidance: use ++ as a statement, not inside a larger expression. print i++, i is legal, correct, and the kind of line that generates a code-review argument. Increment on its own line and the question does not arise.
6. Compound Assignment
Each arithmetic operator has an assignment form. x += 5 means x = x + 5:
awk 'BEGIN { x = 10; x += 5; print x; x -= 3; print x; x *= 2; print x; x /= 4; print x; x %= 4; print x; x ^= 2; print x }'15
12
24
6
2
4Following the chain: 10, +5 → 15, −3 → 12, ×2 → 24, ÷4 → 6, %4 → 2, ^2 → 4.
+= is the one that matters in practice, because it is how every accumulator in this curriculum is written:
awk '{ total += $2 } END { print total + 0 }' area.rpt4870The others exist and are rare. x /= n for normalising and x *= scale for unit conversion are the ones you will occasionally want.
7. Division by Zero Is Fatal
This one surprises people who expect awk to shrug, and it is important because of when it happens:
awk 'BEGIN { print 1/0 }'awk: division by zero
source line number 1That message is on stderr, and the exit status is 2:
awk 'BEGIN { print 1/0 }' 2>/dev/null; echo "rc=$?"rc=2Modulo by zero is the same:
awk 'BEGIN { print 1%0 }' 2>&1; echo "rc=$?"awk: division by zero in mod
source line number 1
rc=2So division by zero is a fatal error, not a warning. That is good news — it is loud, and Chapter 1.6's concern about silent failure does not apply.
The consequence to understand is that it aborts the whole run, mid-stream:
netA 3
netB 0
netC 5awk '{ print "before", NR; print 100 / ($2 - $2) }' fanout.rpt 2>&1before 1
awk: division by zero
input record number 1, file fanout.rpt
source line number 1Record 1's first print ran. Then awk died. Records 2 and 3 were never processed, and any END rule never ran.
Note the error names the input record number and the file, which is genuinely useful for locating the bad data.
The fix is to guard the divisor rather than to catch the error, because awk has no mechanism for catching it:
awk '{ if ($2 + 0 == 0) { print $1, "SKIP (zero divisor)" } else { print $1, 100 / $2 } }' fanout.rptnetA 33.3333
netB SKIP (zero divisor)
netC 20All three records reported, nothing hidden, exit status 0. if/else is Chapter 5.4's subject and is used here because the alternative is a crash.
8. Floating Point
awk has one numeric type and it is double-precision floating point, which brings the usual consequence:
awk 'BEGIN { print 0.1 + 0.2 }'0.3awk 'BEGIN { print (0.1 + 0.2 == 0.3) ? "EQUAL" : "NOT EQUAL" }'NOT EQUALIt prints 0.3 and it is not equal to 0.3. Both outputs are real.
The printing is OFMT's six significant digits (Chapter 5.2, section 12) rounding away a discrepancy that is about seventeen digits down. The comparison sees the full value and finds a difference. So the display actively conceals the thing the comparison is reacting to — which is why this bug is so hard to see by reading output.
The consequence for engineering data: do not compare two computed floating-point values with ==. Compare against a tolerance:
awk 'BEGIN { d = (0.1 + 0.2) - 0.3; if (d < 0) d = -d; print (d < 1e-9) ? "equal within 1e-9" : "genuinely different" }'equal within 1e-9Values read directly from a file and not arithmetically combined are safe to compare with == — a slack of -0.081 read from a report is exactly the double nearest -0.081, and comparing it to the same literal works. It is computed values that drift.
9. String to Number Conversion
Here is the mechanism behind the rest of the chapter. When awk needs a number and has a string, it converts, taking the longest numeric prefix it can and yielding 0 if there is none:
awk 'BEGIN { print "[" ("n/a" + 0) "]", "[" ("12abc" + 0) "]", "[" (" 7 " + 0) "]", "[" ("1e3" + 0) "]", "[" ("" + 0) "]" }'[0] [12] [7] [1000] [0]| Input string | Converts to | Why |
|---|---|---|
"n/a" | 0 | no numeric prefix at all |
"12abc" | 12 | leading digits taken, rest discarded |
" 7 " | 7 | surrounding whitespace ignored |
"1e3" | 1000 | scientific notation recognised |
"" | 0 | empty string, same as unset (Chapter 5.1) |
Signs and decimal points work as expected:
awk 'BEGIN { print ("+5" + 0), ("-3.5" + 0), (".5" + 0), ("5." + 0) }'5 -3.5 0.5 5Not one of these is an error. There is no warning, no stderr output, no change in exit status. "12abc" becoming 12 is the one that should worry you most — a value with a unit suffix, like 1.5ns or 640um, converts to its numeric part silently and plausibly, which is sometimes exactly what you wanted and sometimes a unit error that nothing will catch.
This awk goes further than POSIX requires, and the extras are worth knowing about even though you should not use them:
awk 'BEGIN { print ("0x10" + 0), ("0X1F" + 0), ("inf" + 0), ("nan" + 0) }'16 31 inf nanHexadecimal is converted. So are the floating-point specials. This holds for fields too, not just literals:
0x10awk '{ print $1 + 0 }' hex.txt1610. The Silent Zero
Now the case this chapter exists for. A measurement column with one unusable cell:
alu 120
fifo n/a
uart 75The obvious summary:
awk '{ s += $2; n++ } END { printf "sum %d n %d mean %.2f\n", s, n, s/n }' malformed.rptsum 195 n 3 mean 65.00Look carefully, because the failure here is not where you expect it.
The sum is correct. 120 + 75 = 195, and "n/a" contributed 0, and adding 0 changes nothing. If you spot-check this report by adding the column yourself, it will agree with you.
The mean is wrong. It is 195 ÷ 3 = 65.00, but only two records carried a usable measurement, so the true mean of the available data is 195 ÷ 2 = 97.50. The reported average is low by a third.
The guarded version:
awk '$2 + 0 == 0 && $2 != "0" { bad++; next } { s += $2; n++ } END { printf "sum %d n %d mean %.2f (%d unusable)\n", s, n, s/n, bad + 0 }' malformed.rptsum 195 n 2 mean 97.50 (1 unusable)Same sum. Correct mean. And a count of what it had to throw away.
11. Positive Guards Beat Negative Guards
Section 10's guard works on section 10's data. Add one more realistic bad value and watch it fail:
alu 120
fifo n/a
uart 75
ddr infawk '$2 + 0 == 0 && $2 != "0" { bad++; next } { s += $2; n++ } END { printf "sum %s n %d mean %s (%d unusable)\n", s, n, s/n, bad + 0 }' malformed2.rptsum inf n 3 mean inf (1 unusable)The guard caught n/a and let inf straight through, because "inf" + 0 is infinity, not zero (section 9). And infinity does not just contribute a wrong record — it propagates, so the sum and the mean are both inf. One bad cell destroyed the entire report.
The guard's problem is structural, not a missing case. It is a negative guard: it enumerates what is bad and accepts everything else. That can only ever be as complete as your imagination.
A positive guard inverts it — accept only what matches what a number looks like, reject everything else:
awk '$2 !~ /^-?[0-9]+(\.[0-9]+)?$/ { bad++; next } { s += $2; n++ } END { printf "sum %d n %d mean %.2f (%d unusable)\n", s, n, s/n, bad + 0 }' malformed2.rptsum 195 n 2 mean 97.50 (2 unusable)Correct sum, correct mean, and 2 unusable — it found both. The regex is Module 4's material: an optional minus, one or more digits, optionally a decimal point and more digits, anchored at both ends so nothing extra can hide.
It does not over-reject. On clean data it passes everything:
awk '$2 !~ /^-?[0-9]+(\.[0-9]+)?$/ { bad++; next } { s += $2; n++ } END { printf "sum %d n %d mean %.2f (%d unusable)\n", s, n, s/n, bad + 0 }' area.rptsum 4870 n 3 mean 1623.33 (0 unusable)And it closes the hex hole from section 9 for free:
a 0x10
b 5awk '$2 !~ /^-?[0-9]+(\.[0-9]+)?$/ { print $1, "REJECTED", "[" $2 "]"; next } { print $1, "accepted", $2 }' hex2.rpta REJECTED [0x10]
b accepted 50x10 is rejected rather than silently becoming 16 — and on an awk that converts it to 0 instead, the same script rejects it the same way. The positive guard made the program portable as a side effect of making it correct.
If you need to permit scientific notation, widen the pattern deliberately rather than loosening it:
awk 'BEGIN { for (i = 1; i <= 4; i++) { getline v < "cands.txt"; print v, (v ~ /^-?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?$/) ? "valid" : "rejected" } }'1.5e-9
-3.25
n/a
0x101.5e-9 valid
-3.25 valid
n/a rejected
0x10 rejectedWidening it to admit 1.5e-9 still rejects n/a and 0x10, because the pattern describes a shape rather than listing exclusions. (for is Chapter 5.5 and getline is Chapter 12.6, which measures its three return values; the point here is the pattern.)
12. String Concatenation
awk concatenates by juxtaposition — put two expressions next to each other with no operator:
awk 'BEGIN { a = "clk"; b = 2; print a b; print a "_" b; print a, b }'clk2
clk_2
clk 2Three different things. a b joins with nothing between. a "_" b joins with an underscore. a, b is a print list, joined by OFS — Chapter 5.2, section 8.
The number 2 became the string "2" automatically, using CONVFMT. Conversion runs in both directions without ceremony.
Concatenation has no operator, which is what makes its precedence worth checking rather than assuming. It binds looser than + and -:
awk 'BEGIN { print 1 2 + 3 }'15+ grouped first, giving 1 concatenated with 5. Not 12 + 3 = 15 by coincidence — the result is the two-character string "15", built from 1 and 3 + 2.
awk 'BEGIN { print 10 20 - 5 }'101510 concatenated with 20 - 5. Four characters.
Because arithmetic groups first, the intuitive reading of a labelled result is usually correct:
awk 'BEGIN { a = 5; b = 2; print "diff: " a - b }'diff: 3That works. a - b grouped first and the label survived.
The trap is narrower and sharper than "arithmetic groups loosely". It is that a minus sign following a string is read as binary subtraction, taking that string as its left operand:
awk 'BEGIN { print "val" -1 }'-1The label is gone. awk did not see the string "val" followed by the number -1. It saw "val" - 1 — a subtraction, with "val" converted to 0 by section 9's rule, giving 0 − 1 = −1.
The same thing with a variable:
awk 'BEGIN { n = 3; print "count" -n }'-3Parentheses make the intent unambiguous:
awk 'BEGIN { print "val" (-1) }'val-1And the worst version, because the damage is invisible rather than obvious:
awk 'BEGIN { a = 5; b = 2; print a " " -b }'5-2The author wanted 5 -2. The space is missing, and no label was lost to make the error conspicuous.
What happened: " " became the left operand of the subtraction. It converted to 0, so " " - b is −2, and that was concatenated onto 5, giving "5-2". The separator the author wrote was consumed as an operand and silently became a zero.
The correct forms:
awk 'BEGIN { a = 5; b = 2; print a, -b; print a " " (-b) }'5 -2
5 -2Either a print list, where OFS supplies the separator, or explicit parentheses around the negated value.
13. Counting Is Not Measuring
A closing point that is about engineering rather than awk, and belongs here because this is the chapter where the numbers get made.
awk makes it trivial to sum a column. It does nothing to establish that the sum means what you are about to call it.
awk '{ cells += $2; n++ } END { printf "%d instances, %d cells total\n", n, cells }' area.rpt3 instances, 4870 cells totalThat output is honest. These three would not be:
- "4870 µm² of area." A cell count is not an area. Cells differ in size by more than an order of magnitude between a minimum-width inverter and a wide multi-bit flop, and the conversion depends on the library, the drive strengths chosen and the row height. There is no multiplier that turns one into the other.
- "The design has 4870 cells." Only if the report lists every instance exactly once at one level of hierarchy. A hierarchical report lists a parent and its children, and summing every row double-counts — the caveat Chapter 1.2 established and Chapter 5.1 repeated.
- "Average instance size is 1623 cells." That is the mean of whatever rows were in the file, which is a different quantity from the mean over the design unless the file is complete.
The same discipline applies to the other sums in this module. A total of runtimes is not wall-clock time, because tests run in parallel. A count of failing tests is not a count of bugs, because one root cause fails many tests. A count of violating paths is not a count of problems, because one bad net appears on hundreds of paths.
14. Common Mistakes
Expecting 7/2 to be 3. awk has one numeric type and it is floating point. Use int() if you want truncation.
Reading -2 ^ 2 as 4. It is -4; ^ binds tighter than unary minus.
Reading 2 ^ 3 ^ 2 as 64. It is 512; ^ is right-associative.
Assuming -7 % 2 is 1. It is -1 in awk and C, and 1 in Python. The sign follows the dividend.
Using i++ inside a larger expression. Legal and avoidable. Increment on its own line.
Dividing by a value from input without a guard. Division by zero is fatal, aborts mid-stream, and skips END entirely.
Comparing computed floats with ==. 0.1 + 0.2 prints 0.3 and is not equal to it. Use a tolerance.
Trusting a mean over a column that may contain n/a. The sum stays right and the mean goes wrong — section 10.
Writing a negative guard. It catches the bad values you thought of. inf is the one you did not, and it poisons the whole total.
A negation inside a concatenation without parentheses. a " " -b prints 5-2 — the separator is eaten as an operand.
Calling a cell count an area. Section 13.
15. Exercises
Exercise 1 — Predict the output
awk 'BEGIN { print 2 + 3 * 4 ^ 2 }'50^ first (4² = 16), then * (3 × 16 = 48), then + (2 + 48 = 50). Precedence runs tightest to loosest, exactly as section 3's table lists it.
Exercise 2 — Increment forms
Predict both lines, then run.
awk 'BEGIN { n = 3; print n--, n; m = 3; print --m, m }'3 2
2 2Post-decrement yields the old value then decrements. Pre-decrement decrements then yields the new one. Same structure as ++, section 5.
Exercise 3 — Guard a division
The fanout report contains a zero. Compute 100 / fanout for every net without letting awk abort.
netA 3
netB 0
netC 5awk '{ if ($2 + 0 == 0) print $1, "n/a (zero fanout)"; else printf "%s %.4f\n", $1, 100 / $2 }' fanout.rptnetA 33.3333
netB n/a (zero fanout)
netC 20.0000Note 33.3333 on the first line rather than 33.3333 padded to four places — printf "%.4f" gives 33.3333, which happens to be four decimals already. The guard is the point: without it, record 2 kills the run and netC is never seen.
Exercise 4 — Debug a wrong average
An engineer reports that mean runtime is 65 seconds and a colleague insists it is closer to 100. The data and the command:
alu 120
fifo n/a
uart 75awk '{ s += $2; n++ } END { printf "mean %.2f over %d records\n", s/n, n }' malformed.rptmean 65.00 over 3 recordsBoth engineers are right about their own question, which is the lesson.
The command averaged over 3 records when only 2 carried a measurement. "n/a" + 0 is 0, so fifo added nothing to the sum and one to the count. 195 ÷ 3 = 65; 195 ÷ 2 = 97.5.
awk '$2 !~ /^-?[0-9]+(\.[0-9]+)?$/ { bad++; next } { s += $2; n++ } END { printf "mean %.2f over %d usable records (%d unusable)\n", s/n, n, bad + 0 }' malformed.rptmean 97.50 over 2 usable records (1 unusable)The fixed version reports the denominator it actually used and how many rows it could not read — so the next reader does not have to re-derive the discrepancy.
Exercise 5 — Break a negative guard
Section 10's zero-check guard is given below, along with data containing a second kind of bad value. Predict what it prints before running, then explain the result.
alu 120
fifo n/a
uart 75
ddr infawk '$2 + 0 == 0 && $2 != "0" { bad++; next } { s += $2; n++ } END { printf "sum %s mean %s (%d unusable)\n", s, s/n, bad + 0 }' malformed2.rptsum inf mean inf (1 unusable)"inf" + 0 is infinity, not zero, so the guard's == 0 test did not fire and the record was accepted. Infinity then propagated through +=, so both the sum and the mean are inf — and the guard still reports only 1 unusable, having caught n/a alone.
The repair is not another clause. It is inverting the guard, as in section 11:
awk '$2 !~ /^-?[0-9]+(\.[0-9]+)?$/ { bad++; next } { s += $2; n++ } END { printf "sum %d mean %.2f (%d unusable)\n", s, s/n, bad + 0 }' malformed2.rptsum 195 mean 97.50 (2 unusable)Exercise 6 — Concatenation precedence
Predict all three, then run them. Only one prints what its author intended.
awk 'BEGIN { a = 5; b = 2; print "diff: " a - b }'diff: 3Correct — - binds tighter than concatenation, so a - b grouped first and the label survived.
awk 'BEGIN { a = 5; b = 2; print a " " -b }'5-2Wrong, and quietly so. The author wanted 5 -2. The " " became the left operand of the subtraction, converted to 0, and 0 - 2 = −2 was concatenated onto 5. The separator vanished into an arithmetic operand.
awk 'BEGIN { a = 5; b = 2; print a " " (-b) }'5 -2Parenthesising the negation fixes it. The lesson is not a precedence table to memorise — it is that a - next to a string is a subtraction operator, so anything you meant as a negative value needs parentheses to stay one.
Exercise 7 — Engineering application
From the synthetic area report, produce a summary that states the total, the instance count, the mean, and the unit — in a form a reviewer could audit.
awk '$2 !~ /^-?[0-9]+(\.[0-9]+)?$/ { bad++; next } { cells += $2; n++ } END { if (n == 0) { print "ERROR: no usable records"; exit 2 } printf "%d cells across %d instances; mean %.1f cells/instance", cells, n, cells/n; if (bad) printf " (%d rows unusable)", bad; printf "\n" }' area.rpt4870 cells across 3 instances; mean 1623.3 cells/instanceEverything a reader needs to check the arithmetic: both inputs to the mean, the unit on every quantity, and — had there been any — a count of excluded rows. The n == 0 guard means an empty or wholly unusable file produces an error and a non-zero exit rather than a division by zero.
What it deliberately does not say is anything about area, per section 13.
Exercise 8 — Challenge: a float comparison that works
Two computed values that should be equal. Write a comparison that agrees with the intent rather than with the bits.
awk 'BEGIN { x = 0.1 + 0.2; y = 0.3; print "x prints as", x, "and y prints as", y; print "x == y?", (x == y) ? "yes" : "no" }'x prints as 0.3 and y prints as 0.3
x == y? noBoth print identically and compare unequal, which is section 8's trap in its purest form — the display cannot show you the problem.
awk 'BEGIN { x = 0.1 + 0.2; y = 0.3; d = x - y; if (d < 0) d = -d; printf "difference is %.20f\n", d; print "equal within 1e-9?", (d < 1e-9) ? "yes" : "no" }'difference is 0.00000000000000005551
equal within 1e-9? yesThe %.20f format makes the discrepancy visible: about 5.6 × 10⁻¹⁷, seventeen digits below the value, which OFMT's six significant digits had been rounding away.
For engineering data pick the tolerance from the measurement, not from the floating-point format. Timing reports carry picosecond resolution, so 1e-12 is a defensible epsilon for slack and 1e-9 is sloppy; a cell count should be compared as an integer and needs no tolerance at all.
16. Summary
- Six operators:
+ - * / % ^. There is one numeric type and it is floating point, so7/2is 3.5. ^is right-associative (2^3^2= 512) and binds tighter than unary minus (-2^2= -4). Parenthesise.- Concatenation binds looser than
+and-, so arithmetic groups first — but a-after a string is a subtraction, which is whya " " -bprints5-2. %takes its sign from the dividend —-7 % 2is -1, unlike Python.i++yields the old value,++ithe new. Identical as a statement, which is where they belong.- Division by zero is fatal, exit status 2, and it aborts mid-stream so
ENDnever runs. Guard every divisor that came from input. 0.1 + 0.2prints0.3and is not equal to0.3. Compare computed floats against a tolerance.- Any string converts to a number without complaint —
"n/a"to 0,"12abc"to 12. Never an error. - A malformed measurement corrupts the denominator, not the sum. The total stays checkable and the mean goes wrong, which is the worst possible combination.
- A negative guard misses the value you did not think of, and
infpropagates through a sum. Accept only what matches an anchored numeric pattern, and count the rejects. - State the denominator and name the unit. A cell count is not an area.
You can now compute reliably, and say honestly how much data the computation had.
What every guard in this chapter needed was a way to choose between two actions, and sections 7, 10 and 11 all reached for if before it had been introduced. Chapter 5.4 — if and else covers it properly: the branch forms, the boundary condition that decides whether a score of exactly 80 passes, the braces whose absence changes which statements are conditional, and what happens when a comparison meets the malformed field from section 10.
Where this fits
Part of the awk curriculum.
