fastglmm
Massively scalable generalized linear mixed models
Loading...
Searching...
No Matches
qnorm.h
Go to the documentation of this file.
1#pragma once
2#include <cmath>
3#include <limits>
4#include <algorithm>
5
6// AS241: inverse standard normal CDF (quantile).
7// Returns z such that P(Z <= z) = p (if lower_tail=true).
8// Supports log_p (p given as log(p)).
9inline double qnorm_as241(double p,
10 double mean = 0.0,
11 double sd = 1.0,
12 bool lower_tail = true,
13 bool log_p = false)
14{
15 if (!(sd > 0.0) || !std::isfinite(sd)) {
16 return std::numeric_limits<double>::quiet_NaN();
17 }
18
19 // Handle log_p and tail choice
20 double pp;
21 if (log_p) {
22 if (p > 0.0) return std::numeric_limits<double>::quiet_NaN(); // log(p) must be <= 0
23 pp = std::exp(p);
24 } else {
25 pp = p;
26 }
27
28 if (!lower_tail) pp = 1.0 - pp;
29
30 // Bounds
31 if (pp <= 0.0) return -std::numeric_limits<double>::infinity();
32 if (pp >= 1.0) return std::numeric_limits<double>::infinity();
33
34 // Coefficients (AS241)
35 static const double a[8] = {
36 3.3871328727963666080,
37 1.3314166789178437745e+2,
38 1.9715909503065514427e+3,
39 1.3731693765509461125e+4,
40 4.5921953931549871457e+4,
41 6.7265770927008700853e+4,
42 3.3430575583588128105e+4,
43 2.5090809287301226727e+3
44 };
45 static const double b[8] = {
46 1.0,
47 4.2313330701600911252e+1,
48 6.8718700749205790830e+2,
49 5.3941960214247511077e+3,
50 2.1213794301586595867e+4,
51 3.9307895800092710610e+4,
52 2.8729085735721942674e+4,
53 5.2264952788528545610e+3
54 };
55 static const double c[8] = {
56 1.42343711074968357734,
57 4.63033784615654529590,
58 5.76949722146069140550,
59 3.64784832476320460504,
60 1.27045825245236838258,
61 2.41780725177450611770e-1,
62 2.27238449892691845833e-2,
63 7.74545014278341407640e-4
64 };
65 static const double d[8] = {
66 1.0,
67 2.05319162663775882187,
68 1.67638483018380384940,
69 6.89767334985100004550e-1,
70 1.48103976427480074590e-1,
71 1.51986665636164571966e-2,
72 5.47593808499534494600e-4,
73 1.05075007164441684324e-9
74 };
75 static const double e[8] = {
76 6.65790464350110377720,
77 5.46378491116411436990,
78 1.78482653991729133580,
79 2.96560571828504891230e-1,
80 2.65321895265761230930e-2,
81 1.24266094738807843860e-3,
82 2.71155556874348757815e-5,
83 2.01033439929228813265e-7
84 };
85 static const double f[8] = {
86 1.0,
87 5.99832206555887937690e-1,
88 1.36929880922735805310e-1,
89 1.48753612908506148525e-2,
90 7.86869131145613259100e-4,
91 1.84631831751005468180e-5,
92 1.42151175831644588870e-7,
93 2.04426310338993978564e-15
94 };
95
96 auto poly = [](const double* coef, int n, double x) {
97 // Horner: coef[0] + coef[1] x + ... + coef[n-1] x^(n-1)
98 double v = coef[n - 1];
99 for (int i = n - 2; i >= 0; --i) v = v * x + coef[i];
100 return v;
101 };
102
103 constexpr double split1 = 0.425;
104 constexpr double split2 = 5.0;
105 constexpr double const1 = 0.180625;
106 constexpr double const2 = 1.6;
107
108 double q = pp - 0.5;
109 double z;
110
111 if (std::fabs(q) <= split1) {
112 // Central region
113 double r = const1 - q*q;
114 z = q * poly(a, 8, r) / poly(b, 8, r);
115 } else {
116 // Tails
117 double r = (q < 0.0) ? pp : (1.0 - pp);
118 r = std::sqrt(-std::log(r));
119
120 if (r <= split2) {
121 r -= const2;
122 z = poly(c, 8, r) / poly(d, 8, r);
123 } else {
124 r -= split2;
125 z = poly(e, 8, r) / poly(f, 8, r);
126 }
127 if (q < 0.0) z = -z;
128 }
129
130 return mean + sd * z;
131}
double qnorm_as241(double p, double mean=0.0, double sd=1.0, bool lower_tail=true, bool log_p=false)
Definition qnorm.h:9