A numerical comparison needs more than two values. It needs a justified reference, a metric, units, tolerances, and a statement of what passing means. This tutorial compares the same constructed pressure results with absolute, relative, and mixed criteria.
Compare absolute-only, relative-only, and mixed decisions.
Change a tolerance and inspect the changed claim.
Test boundaries and non-finite values.
Compare collection summaries.
Record the evidence and limitations.
State the requirement first
The values below are constructed pressure results chosen to expose different scales; they are not measurements from an instrument. Treat each listed binary64 value as the designated reference for this tutorial. The activity does not assess decimal conversion or claim that these are exact physical values.
The hypothetical requirement is:
Accept a computed pressure when its discrepancy is no greater than an absolute allowance of \(10^{-5}\ \mathrm{Pa}\) plus a relative allowance of \(10^{-6}\) times the reference magnitude.
The absolute tolerance has units of pascals. The relative tolerance is dimensionless. Before running the comparisons, predict which cases an absolute-only, relative-only, and mixed criterion will accept.
import mathimport sysabsolute_tolerance_pa =1.0e-5relative_tolerance =1.0e-6cases = [ {"case": "zero", "reference_pa": 0.0, "computed_pa": 5.0e-6}, {"case": "near zero", "reference_pa": 1.0e-6, "computed_pa": 6.0e-6}, {"case": "order one", "reference_pa": 1.0, "computed_pa": 1.00002}, {"case": "large", "reference_pa": 1.0e6, "computed_pa": 1.0e6+0.5},]def absolute_error(computed, reference):"""Return absolute error in the units of the operands."""returnabs(computed - reference)def relative_error(computed, reference):"""Return relative error, or None when the reference is zero."""if reference ==0.0:returnNonereturn absolute_error(computed, reference) /abs(reference)def allowed_discrepancy(reference, absolute_tolerance, relative_tolerance):"""Return a reference-anchored mixed tolerance.""" tolerances_are_valid = ( math.isfinite(absolute_tolerance)and math.isfinite(relative_tolerance)and absolute_tolerance >=0.0and relative_tolerance >=0.0 )ifnot tolerances_are_valid:raiseValueError("Tolerances must be finite and non-negative.")return absolute_tolerance + relative_tolerance *abs(reference)def within_tolerance( computed, reference, absolute_tolerance, relative_tolerance,):"""Apply the finite, reference-anchored mixed comparison policy."""ifnot (math.isfinite(computed) and math.isfinite(reference)):returnFalsereturn absolute_error(computed, reference) <= allowed_discrepancy( reference, absolute_tolerance, relative_tolerance, )print(f"absolute tolerance: {absolute_tolerance_pa:.1e} Pa")print(f"relative tolerance: {relative_tolerance:.1e}")
absolute tolerance: 1.0e-05 Pa
relative tolerance: 1.0e-06
The helper functions make the convention explicit: the reference sets the relative scale, equality with the tolerance boundary passes, and non-finite operands are rejected. A different scientific contract may choose differently, but it should do so deliberately.
Compute absolute and relative error
Absolute error retains pascals. Relative error is dimensionless and is reported as undefined when the designated reference is zero.
case reference (Pa) computed (Pa) absolute (Pa) relative
zero 0 5e-06 5e-06 undefined
near zero 1e-06 6e-06 5e-06 5
order one 1 1.00002 2e-05 2e-05
large 1000000 1000000.5 0.5 5e-07
The zero and near-zero cases have the same absolute error, \(5\times10^{-6}\ \mathrm{Pa}\). Relative error is undefined for the zero reference and equals approximately 5 for the near-zero reference. The large case has the largest absolute error, \(0.5\ \mathrm{Pa}\), but the smallest relative error, \(5\times10^{-7}\).
Compare three criteria
The absolute-only criterion asks whether the discrepancy is below the absolute floor. The relative-only criterion asks whether it is within one part per million of a nonzero reference. The mixed criterion applies the requirement as stated.
case abs only rel only mixed limit (Pa) mixed
zero pass N/A 1e-05 pass
near zero pass fail 1e-05 pass
order one fail fail 1.1e-05 fail
large fail pass 1.00001 pass
The mixed criterion does not conceal a special rule for zero. Its absolute term dominates there, while its relative term permits a scale-appropriate discrepancy for the large reference. The order-one case fails because its error exceeds the stated combined allowance.
Why machine epsilon answers another question
Machine epsilon describes binary64 spacing near one. Compare that format property with the allowed pressure discrepancy derived from the stated requirement.
binary64 machine epsilon: 2.22045e-16
case error (Pa) mixed allowance (Pa) error <= epsilon?
zero 5e-06 1e-05 False
near zero 5e-06 1e-05 False
order one 2e-05 1.1e-05 False
large 0.5 1.00001 False
Using machine epsilon as an absolute pressure tolerance rejects every case, but that result has no physical rationale: machine epsilon is dimensionless, while the discrepancies are measured in pascals. A multiplier chosen only to obtain the desired pass/fail pattern would not repair the missing justification.
Exercise: change the tolerance claim
Tighten exercise_absolute_tolerance_pa to \(10^{-6}\ \mathrm{Pa}\) while keeping the relative tolerance unchanged. Predict which mixed decisions will change, then run the cell. Remember that editing the tolerance changes the requirement; it does not improve any computed value.
exercise_absolute_tolerance_pa =1.0e-5# Try 1.0e-6.exercise_relative_tolerance =1.0e-6for case in cases: accepted = within_tolerance( case["computed_pa"], case["reference_pa"], exercise_absolute_tolerance_pa, exercise_relative_tolerance, )print(f"{case['case']:>10s}: {'pass'if accepted else'fail'}")
zero: pass
near zero: pass
order one: fail
large: pass
Check the exercise
The comparison below shows the baseline and tightened decisions side by side. Explain why the zero and near-zero cases change while the large case does not.
for case in cases: baseline_pass = within_tolerance( case["computed_pa"], case["reference_pa"],1.0e-5,1.0e-6, ) tightened_pass = within_tolerance( case["computed_pa"], case["reference_pa"],1.0e-6,1.0e-6, )print(f"{case['case']:>10s}: baseline = "f"{'pass'if baseline_pass else'fail'}, tightened = "f"{'pass'if tightened_pass else'fail'}" )
The criterion uses <=, so a value exactly on the boundary passes. The next representable value above that boundary fails. The explicit finite-value policy rejects NaN and infinity before arithmetic comparisons can obscure the intent.
exactly on boundary: True
next value above boundary: False
computed = nan: False
computed = inf: False
computed = -inf: False
Compare a collection
Suppose a ten-point pressure profile must be within \(0.5\ \mathrm{Pa}\) at every point. Nine computed values have errors of \(0.1\ \mathrm{Pa}\) and one has an error of \(1.0\ \mathrm{Pa}\). Predict whether maximum error and root-mean-square error lead to the same decision.
maximum absolute error: 1.000 Pa
root-mean-square error: 0.330 Pa
mean absolute error: 0.190 Pa
maximum criterion passes: False
RMS criterion passes: True
The RMS value passes \(0.5\ \mathrm{Pa}\) because it describes a typical squared error across the collection. The maximum fails because one point violates the per-point requirement. RMS is not defective; it answers a different question. For this stated requirement, the component-wise or maximum criterion is the relevant one.
Evidence record
A reviewable comparison records the reference status, units, metric, tolerance rationale, boundary policy, result, and limitations together.
evidence = {"reference_status": ("Designated constructed binary64 values for this tutorial." ),"quantity_and_units": "Pressure in pascals.","scalar_criterion": ("abs(computed - reference) <= atol + rtol * abs(reference)" ),"absolute_tolerance_pa": absolute_tolerance_pa,"relative_tolerance": relative_tolerance,"rationale": ("Hypothetical 1e-5 Pa near-zero floor plus a one-part-per-million ""allowance; not inferred from machine epsilon." ),"nonfinite_policy": "Reject NaN and infinity.","mixed_decisions": { result["case"]: result["mixed_pass"]for result in criterion_results },"collection_requirement": "Every profile point within 0.5 Pa.","collection_maximum_error_pa": maximum_error_pa,"collection_rms_error_pa": rms_error_pa,"limitation": ("Constructed references isolate comparison behaviour; no measurement ""uncertainty, model error, or algorithmic cause was assessed." ),}evidence
{'reference_status': 'Designated constructed binary64 values for this tutorial.',
'quantity_and_units': 'Pressure in pascals.',
'scalar_criterion': 'abs(computed - reference) <= atol + rtol * abs(reference)',
'absolute_tolerance_pa': 1e-05,
'relative_tolerance': 1e-06,
'rationale': 'Hypothetical 1e-5 Pa near-zero floor plus a one-part-per-million allowance; not inferred from machine epsilon.',
'nonfinite_policy': 'Reject NaN and infinity.',
'mixed_decisions': {'zero': True,
'near zero': True,
'order one': False,
'large': True},
'collection_requirement': 'Every profile point within 0.5 Pa.',
'collection_maximum_error_pa': 1.0,
'collection_rms_error_pa': 0.330151480384382,
'limitation': 'Constructed references isolate comparison behaviour; no measurement uncertainty, model error, or algorithmic cause was assessed.'}
Pitfalls and optional extensions
Do not replace a zero relative-error denominator with an undocumented epsilon.
Do not copy a library’s default tolerances without checking its formula and relating both terms to the scientific requirement.
Do not let NaN pass or fail only because of incidental comparison semantics; state the policy explicitly.
Add a value exactly on the relative part of a nonzero boundary and verify the <= convention.
Reverse the roles of computed and reference values to observe that this reference-anchored criterion is directional.
Construct a profile for which maximum error passes but a derived observable, such as an integrated total, violates its own requirement.