diff --git a/src/lib.rs b/src/lib.rs index c26971f..49237fa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -863,6 +863,30 @@ mod tests { /// The draw margin must grow with the draw probability. It did not: it ran /// 0.674 -> 1.476 -> 0.503 -> 0.982 as `p_draw` went 0.5 -> 0.9 -> 0.99 -> /// 0.999, which is not a rounding error but a broken function. + /// Deep in the tail the accuracy limit is the *caller's* argument, not this + /// function. + /// + /// `compute_margin(0.999999, ..)` computes `1.0 - p_draw`, and 0.999999 is + /// not representable: the subtraction cancels and leaves 2.9e-11 of + /// relative error in the argument before `erfc_inv` is even entered. Given + /// an exactly-representable argument the result is good to 1.8e-16, so this + /// is inherent to taking `p_draw` near one rather than something to fix + /// here. At `p_draw = 0.999` the whole path is still accurate to 4e-16. + /// + /// Worth pinning: measured against a 70-digit reference, `puruspe`'s + /// `inverfc` returns the identical wrong value for the identical reason, + /// which is what makes it clear the fault is upstream of both. + #[test] + fn erfc_inv_is_exact_given_an_exactly_representable_argument() { + // erfc(z / sqrt2) = 1e-6 exactly, so z = Phi^-1(0.9999995). + let got = SQRT_2 * erfc_inv(1e-6); + let exact = 4.891_638_475_698_59; + assert!( + (got - exact).abs() / exact < 1e-14, + "got {got}, exact {exact}" + ); + } + #[test] fn compute_margin_is_monotone_in_the_draw_probability() { let mut previous = 0.0;