Unit 02.05: Decimal equality is the comparison that lies
Arithmetic in R holds no surprises. Comparing two decimals for equality holds one, and it is the same surprise in every language you will ever use.
Floating point, and comparison against the unknown
The operators are conventional, with two worth naming: %/% is integer division and %% is the remainder. They are how you ask 'how many whole weeks' and 'how many days left over'.
Decimals are stored in binary, and most decimal fractions have no exact binary form โ in the same way that one third has no exact decimal form. So arithmetic on them accumulates a tiny error, and == compares exactly. all.equal() is the tool that compares within a tolerance.
The second issue is comparison with NA. It gives NA, and NA then spreads through anything that consumes it.
This block prints the integer operators, then the classic equality failure, then a comparison against missing data.
# Arithmetic is ordinary. Comparison of decimals is not.
cat("Integer division 17 %/% 5 :", 17 %/% 5, "\n")
cat("Remainder 17 %% 5 :", 17 %% 5, "\n")
cat("Power 2 ^ 10 :", 2 ^ 10, "\n\n")
# The one that catches everybody:
cat("0.1 + 0.2 == 0.3 gives:", 0.1 + 0.2 == 0.3, "\n")
cat("printed, it looks like:", 0.1 + 0.2, "\n")
cat("the actual difference is:", format(0.1 + 0.2 - 0.3, scientific = TRUE), "\n\n")
cat("all.equal() is the right test:", isTRUE(all.equal(0.1 + 0.2, 0.3)), "\n\n")
# Comparison with NA propagates rather than failing.
scores <- c(72, NA, 86)
cat("scores > 80 gives:", paste(scores > 80, collapse = " "), "\n")
cat("sum() of that :", sum(scores > 80), "\n")
cat("with na.rm=TRUE :", sum(scores > 80, na.rm = TRUE), "\n")
0.1 + 0.2 == 0.3 is FALSE, while printing the sum shows 0.3 โ R rounds for display and compares exactly. The real difference is 5.551115e-17, far too small to see and quite large enough to fail a test. all.equal() returns TRUE. At the end, scores > 80 gives FALSE NA TRUE, and sum() of that is NA: one unknown makes the total unknown. With na.rm = TRUE it is 1.
The mistake this prevents
The mistake is if (total == 100) on computed decimals. It fails occasionally and unpredictably, which is far worse than failing every time.
Takeaway
Use %/% and %% when you mean whole parts and remainders. Never compare computed decimals with == โ use all.equal(). Remember that any comparison involving NA returns NA, and decide explicitly what should happen to those rows.
