# Module 5.2: Errors
# Run sections in order while following the Quarto page.

# Meeting 1: absolute and relative error
reference <- 6.239
approximation <- 6.24
absolute_error <- abs(approximation - reference)
relative_error <- absolute_error / abs(reference)
absolute_error
100 * relative_error  # percent

# Binary floating-point numbers are not exact decimal fractions.
0.1 + 0.2
0.3
0.1 + 0.2 == 0.3
sprintf("%.17f", 0.1 + 0.2)
sprintf("%.17f", 0.3)
(0.1 + 0.2) - 0.3

# Compare with a tolerance chosen for this small example.
tolerance <- 1e-12
abs((0.1 + 0.2) - 0.3) < tolerance

# The order of subtraction can leave a tiny floating-point remainder.
9.1 - 9 - 0.1
9.1 - 0.1 - 9

# Repeated addition can accumulate a small error.
number_of_steps <- 100000
delta_t <- 0.1
time_by_addition <- 0
for (i in 1:number_of_steps) {
  time_by_addition <- time_by_addition + delta_t
}
time_from_step_number <- number_of_steps * delta_t
time_by_addition
time_from_step_number
time_by_addition - time_from_step_number

# Meeting 2: finite range
1e308 * 10      # overflow: Inf
1e-300 / 1e100  # underflow: 0

# Equivalent mathematical expressions need not produce the same result.
a <- 1e16
b <- -1e16
c <- 1
(a + b) + c
a + (b + c)

# Compare Euler's finite-step approximation with an analytical solution.
# P(0) = 100 and dP/dt = 0.1 P for 8 hours.
delta_t_values <- c(1, 0.5, 0.1, 0.01)
exact_population <- 100 * exp(0.1 * 8)
euler_population <- 100 *
  (1 + 0.1 * delta_t_values)^(8 / delta_t_values)
comparison <- data.frame(
  delta_t = delta_t_values,
  euler_population = euler_population,
  absolute_error = abs(euler_population - exact_population)
)
exact_population
comparison
