Theory

The division trap — a 0% book

Now a mistake that does not look like a mistake. Say you have read 190 pages out of 380 and want the progress as a percentage:

progress := it.PagesRead / it.Pages * 100
fmt.Println("Progress:", progress, "%")

The result:

Progress: 0 %

Zero percent. You read half the book and the program says 0 — with no error and no warning. Why? PagesRead and Pages are both int, and integer division throws away the remainder: 190 / 380 = 0 (the whole part), then 0 * 100 = 0. The program computed "correctly" — by int rules.

The cure is an explicit conversion to float64 BEFORE dividing:

progress := float64(it.PagesRead) / float64(it.Pages) * 100
fmt.Printf("Progress: %.1f%%\n", progress)
Progress: 50.0%

Go never converts types silently — an int will not become a float64 on its own. That feels pedantic, but it is exactly why the bug is always visible in the code: wherever there is no float64(...), the division is integer division.

Briefly on characters: a string stores bytes; a rune is one Unicode character. len("ąžuolas") returns bytes (9), not letters (7) — Lithuanian letters take 2 bytes each. If you ever count letters: len([]rune(s)).

Gotcha. The int-division bug is dangerous because it is silent: an expense average, a rating average, a percentage — they all "work", they just show the wrong answer. The rule: when you see /, ask yourself — do I want the remainder thrown away here? If not, convert to float64 before the division (not after: float64(a/b) is too late — the division already happened in int world).