# 6.x Chapter 6 summary and quiz
> Precedence, arithmetic, comparisons, and logic reviewed, plus a change-counting program that needs the whole chapter.
Source: https://learnrust.net/chapter-6/chapter-6-summary-and-quiz/
This was the chapter where five chapters of operator credit got settled: every symbol you'd been using on faith now has rules you've seen stated. The review, some predictions, and a program about money.
## Quick review
**Precedence** ranks the operators (arithmetic above comparisons above `&&` above `||`, assignment dead last) and **associativity** breaks ties between equals, almost always left-to-right. You memorize neither: parenthesize anything non-obvious instead, the one rule worth keeping. Comparisons have *no* associativity, so chains like `1 <= x <= 10` are compile errors with the `&&` fix in the help text. Precedence governs grouping only; operands themselves evaluate left to right, guaranteed by the language, function arguments included.
The **arithmetic operators** behave like math class until types intervene. Integer division **truncates** toward zero (`7 / 2` is `3`, `-7 / 2` is `-3`), float division doesn't, and one stray integer literal can truncate the accuracy out of a float formula. The **remainder** `%` reports what's left after truncating division, takes its sign from the left operand (so test evenness with `!= 0`, not `== 1`), and `rem_euclid` exists when math-class modulo is wanted. Dividing by zero is a compile error when the compiler can prove it and a panic (debug *and* release) when it can't. There is no exponent operator: `.pow()` for integers (overflow rules per lesson 4.4), `.powi()`/`.powf()` for floats, and `^` is XOR, silently computing `8` from `2 ^ 10` for anyone who guesses otherwise.
**Compound assignment** (`+=`, `-=`, `*=`, `/=`, `%=`, plus bitwise cousins) folds an operation into a `mut` variable without naming it twice, evaluates to `()` like all Rust assignment, and stands in for the `++`/`--` that Rust deliberately omitted; `x++` gets a custom compiler error with the fix attached, while `--x` legally double-negates and does nothing, the one C habit to actively hunt down.
The six **comparison operators** produce `bool`s from same-typed operands, with no `== true` redundancies wanted. Float *ordering* is dependable when values are far apart; float *equality* is not dependable at all once values have been computed, since `0.1 + 0.2 == 0.3` is `false`. The professional tool is **epsilon** comparison: absolute tolerance fails across magnitudes, relative tolerance (Knuth) fails near zero, so `approx_equal` checks absolute first and relative second. `f64::EPSILON` is machine epsilon, not a general-purpose tolerance, and `NaN` answers `false` to every comparison, so use `.is_nan()`.
The **logical operators** `&&` and `||` combine `bool`s, with each side required to *be* a `bool` (the C-classic `x == 'y' || 'Y'` is a type error here). Both **short-circuit**: a decided left side skips the right entirely, which makes `count != 0 && total / count > n` a real guard, and makes side effects on the right side unreliable. AND outranks OR, so parenthesize when they mix, and **De Morgan's laws** flip the connector when a `!` pushes through: `!(a && b)` is `!a || !b`, with each inner comparison flipping too.
(The optional lesson, [6.6](@/chapter-6/bitwise-operators.md): the same symbols `&`, `|`, `^`, `!`, plus shifts, applied per-bit; masks built from `1 << n`; set with `|=`, clear with `&= !MASK`, toggle with `^=`, test with `& MASK != 0`.)
## Quiz time
**Question #1**
Add the parentheses the compiler implies:
a) `2 * 3 + 4 % 5`
b) `x > 0 && x % 2 == 0`
c) `a || !b && c`
Show solution
a) `(2 * 3) + (4 % 5)` (`*` and `%` share the top arithmetic rank; `+` waits)
b) `(x > 0) && ((x % 2) == 0)` (arithmetic, then comparisons, then `&&`)
c) `a || ((!b) && c)` (`!` grabs tightest, `&&` beats `||`; this one deserves its parentheses in real code)
**Question #2**
What does this print?
```rust
fn main() {
println!("{}", 13 / 4);
println!("{}", 13 % 4);
println!("{}", -13 / 4);
println!("{}", 13.0 / 4.0);
}
```
Show solution
```
3
1
-3
3.25
```
Integer division truncates toward zero in both directions (-3.25 truncates to -3); the remainder line is the leftover from line one (three 4s fit in 13, one left); the float line is the only one doing math-class division.
**Question #3**
Three claims, true or false, one sentence of why:
a) If `f(x)` has a side effect, `f(x) || true` guarantees the side effect happens.
b) `(a - b).abs() < f64::EPSILON` is the professional way to compare two computed `f64`s.
c) `n % 2 == 1` correctly detects odd numbers for every `i32` value of `n`.
Show solution
a) True, but only by the skin of its teeth: `||` evaluates its *left* side always (it's the right side short-circuiting skips), so `f(x)` runs. Flip the operands and it might not.
b) False: that's machine epsilon, a near-zero tolerance that fails once values accumulate normal rounding error; use the combined absolute-plus-relative `approx_equal` from lesson 6.4.
c) False: negative odd numbers give a remainder of `-1`, not `1`, because `%` takes the dividend's sign; `n % 2 != 0` works for both signs.
**Question #4**
The chapter's program: a cash register. Read the cost and the amount paid (both in whole cents, using the read-a-number recipe from lesson 6.4), and either report how much is still owed, or break the change due into quarters (25), dimes (10), nickels (5), and pennies. Compound assignment will earn its keep. Match the sample run exactly:
```
Cost in cents?
289
Paid in cents?
500
Change due: 211 cents
quarters: 8
dimes: 1
nickels: 0
pennies: 1
```
And if they underpay:
```
Cost in cents?
500
Paid in cents?
289
You still owe 211 cents.
```
Show solution
```rust
use std::io;
fn read_cents(prompt: &str) -> u32 {
println!("{prompt}");
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("failed to read input");
input.trim().parse().expect("that wasn't a whole number")
}
fn main() {
let cost = read_cents("Cost in cents?");
let paid = read_cents("Paid in cents?");
if paid < cost {
println!("You still owe {} cents.", cost - paid);
} else {
let mut change = paid - cost;
println!("Change due: {change} cents");
let quarters = change / 25;
change %= 25;
let dimes = change / 10;
change %= 10;
let nickels = change / 5;
let pennies = change % 5;
println!("quarters: {quarters}");
println!("dimes: {dimes}");
println!("nickels: {nickels}");
println!("pennies: {pennies}");
}
}
```
Design notes, lesson by lesson: each coin is one integer division ("how many whole quarters fit?", 6.2) followed by one compound remainder assignment ("keep what's left", 6.3), and the cascade repeats per denomination. The `paid < cost` comparison (6.4) does double duty: it picks the message, *and* it makes both subtractions safe, since `u32` subtraction would panic on a negative result (lesson 4.4's overflow rules; each branch only subtracts the smaller from the larger). The `read_cents` helper is lesson 5.6's recipe behind a function from chapter 2.
**Question #5**
A reviewer suggests "the program would be friendlier in dollars: read `f64`s like `2.89` instead of cents." What goes wrong?
Show solution
Money amounts like `2.89` aren't exactly representable in binary floats (lesson 6.4), so the arithmetic accumulates sub-cent errors, and the coin-counting logic depends on *exact* division and remainder, which floats can't promise (worse: `%` and `/` on floats wouldn't truncate to whole coins at all without extra conversion). Whole cents in an integer make every operation exact. This is a general rule worth keeping: count money in its smallest unit, in integers. If friendliness matters, read dollars as *text*, convert to cents once, carefully, and do all arithmetic there.
Chapter 7 puts these operators where they live in real programs: conditions that branch and loops that repeat, including your first program that runs until *it* decides to stop.