Problem 478363 · medium · Level 04 Non-Linear Data Structures

Measurements That Carry Their Error

py-dunder · reflected operators · propagation of uncertainty · numeric types

In a physics lab every measurement has an uncertainty: a rod is 2.00 ± 0.01 m long, a swing takes 2.84 ± 0.02 s. When measurements are combined in a formula, their uncertainties combine too, and students keep getting that bookkeeping wrong. A number type that does it automatically lets them write the formula as it is.

Write a class Measured(value, error), where error >= 0 (a negative error raises ValueError). The attributes value and error can be read. Treat every operand as an independent measurement and use these rules, where a and b are measurements with errors ea and eb:

  • a + b and a - b: error sqrt(ea**2 + eb**2);
  • a * b: error sqrt((b * ea)**2 + (a * eb)**2) (using the values of a and b);
  • a / b: error sqrt((ea / b)**2 + (a * eb / b**2)**2);
  • a ** n for a plain number n: error abs(n * a**(n - 1)) * ea;
  • -a keeps the error.

A plain number (int or float) is an exact value with error 0, and may appear on either side of +, -, * and / (so 2 * a, 1 - a and 1 / a work). sum(list_of_measurements) must work. Anything else raises TypeError.

Also write:

  • a.relative, the relative error error / abs(value), read without brackets;
  • a.agrees(b, k=2): whether abs(a.value - b.value) <= k * sqrt(ea**2 + eb**2);
  • repr(a): Measured(2.0, 0.01) (both numbers as repr shows them);
  • str(a): with d = max(0, 1 - floor(log10(error))) decimals (two significant figures of the error), f"{value:.{d}f} ± {error:.{d}f}"; with an error of 0, just str(value).

The setup's helper ve(m) returns (m.value, m.error), pendulum_runs(n, seed) generates timing measurements, and raises(fn, *args) returns the name of the exception a call raises (or None).

Examples

Input:  L, T = Measured(2.0, 0.01), Measured(2.84, 0.02)
        g = 4 * math.pi ** 2 * L / T ** 2
        str(g), ve(L + T), repr(-L), str(1 / T)
Output: ('9.79 ± 0.15', (4.84, 0.022360679774997897), 'Measured(-2.0, 0.01)', '0.3521 ± 0.0025')

Constraints

  • Values and errors are floats or integers; divisors are not zero, and a value raised to a power is positive.
  • Floats are compared with a tolerance of 1e-6.

Goals

  • Write the arithmetic special methods, including the reflected ones, for a numeric value type
  • Let plain numbers and your objects mix on either side of an operator
  • Carry a derived quantity (the error) through every operation automatically
Starting Python…