Skip to main content

finance_solution/derivatives/
norm.rs

1//! Standard normal PDF and CDF (no external special-function crate).
2
3/// φ(x) = (1/√(2π)) exp(−x²/2)
4#[inline]
5pub fn norm_pdf(x: f64) -> f64 {
6    const INV_SQRT_2PI: f64 = 0.398_942_280_401_432_7;
7    INV_SQRT_2PI * (-0.5 * x * x).exp()
8}
9
10/// Φ(x) — standard normal CDF (Abramowitz & Stegun 26.2.17 style).
11#[inline]
12pub fn norm_cdf(x: f64) -> f64 {
13    if x.is_nan() {
14        return f64::NAN;
15    }
16    if x > 8.0 {
17        return 1.0;
18    }
19    if x < -8.0 {
20        return 0.0;
21    }
22    // A&S 26.2.17
23    const B0: f64 = 0.231_641_9;
24    const B1: f64 = 0.319_381_530;
25    const B2: f64 = -0.356_563_782;
26    const B3: f64 = 1.781_477_937;
27    const B4: f64 = -1.821_255_978;
28    const B5: f64 = 1.330_274_429;
29
30    let t = 1.0 / (1.0 + B0 * x.abs());
31    let poly = ((((B5 * t + B4) * t + B3) * t + B2) * t + B1) * t;
32    let pdf = norm_pdf(x.abs());
33    let tail = pdf * poly;
34    if x >= 0.0 {
35        1.0 - tail
36    } else {
37        tail
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn pdf_at_zero() {
47        let p = norm_pdf(0.0);
48        assert!((p - 0.398_942_280_4).abs() < 1e-9);
49    }
50
51    #[test]
52    fn cdf_symmetry() {
53        assert!((norm_cdf(0.0) - 0.5).abs() < 1e-7);
54        assert!((norm_cdf(1.0) + norm_cdf(-1.0) - 1.0).abs() < 1e-6);
55        // Φ(1.96) ≈ 0.975
56        assert!((norm_cdf(1.96) - 0.975).abs() < 5e-4);
57    }
58}