/src/r-source/src/nmath/qt.c
Line | Count | Source |
1 | | /* |
2 | | * Mathlib : A C Library of Special Functions |
3 | | * Copyright (C) 2000-2022 The R Core Team |
4 | | * Copyright (C) 2003-2022 The R Foundation |
5 | | * Copyright (C) 1998 Ross Ihaka |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 2 of the License, or |
10 | | * (at your option) any later version. |
11 | | * |
12 | | * This program is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, a copy is available at |
19 | | * https://www.R-project.org/Licenses/ |
20 | | * |
21 | | * DESCRIPTION |
22 | | * |
23 | | * The "Student" t distribution quantile function. |
24 | | * |
25 | | * NOTES |
26 | | * |
27 | | * This is a C translation of the Fortran routine given in: |
28 | | * Hill, G.W (1970) "Algorithm 396: Student's t-quantiles" |
29 | | * CACM 13(10), 619-620. |
30 | | * |
31 | | * Supplemented by inversion for 0 < ndf < 1. |
32 | | * |
33 | | * ADDITIONS: |
34 | | * - lower_tail, log_p |
35 | | * - using expm1() : takes care of Lozy (1979) "Remark on Algo.", TOMS |
36 | | * - Apply 2-term Taylor expansion as in |
37 | | * Hill, G.W (1981) "Remark on Algo.396", ACM TOMS 7, 250-1 |
38 | | * - Improve the formula decision for 1 < df < 2 |
39 | | */ |
40 | | |
41 | | #include "nmath.h" |
42 | | #include "dpq.h" |
43 | | |
44 | | double qt(double p, double ndf, int lower_tail, int log_p) |
45 | 0 | { |
46 | 0 | const static double eps = 1.e-12; |
47 | |
|
48 | 0 | double P, q; |
49 | |
|
50 | 0 | #ifdef IEEE_754 |
51 | 0 | if (ISNAN(p) || ISNAN(ndf)) |
52 | 0 | return p + ndf; |
53 | 0 | #endif |
54 | | |
55 | 0 | R_Q_P01_boundaries(p, ML_NEGINF, ML_POSINF); |
56 | |
|
57 | 0 | if (ndf <= 0) ML_WARN_return_NAN; |
58 | |
|
59 | 0 | if (ndf < 1) { /* based on qnt */ |
60 | 0 | const static double accu = 1e-13; |
61 | 0 | const static double Eps = 1e-11; /* must be > accu */ |
62 | |
|
63 | 0 | double ux, lx, nx, pp; |
64 | |
|
65 | 0 | int iter = 0; |
66 | |
|
67 | 0 | p = R_DT_qIv(p); |
68 | | |
69 | | /* Invert pt(.) : |
70 | | * 1. finding an upper and lower bound */ |
71 | 0 | if(p > 1 - DBL_EPSILON) return ML_POSINF; |
72 | 0 | pp = fmin2(1 - DBL_EPSILON, p * (1 + Eps)); |
73 | 0 | for(ux = 1.; ux < DBL_MAX && pt(ux, ndf, TRUE, FALSE) < pp; ux *= 2); |
74 | 0 | pp = p * (1 - Eps); |
75 | 0 | for(lx =-1.; lx > -DBL_MAX && pt(lx, ndf, TRUE, FALSE) > pp; lx *= 2); |
76 | | |
77 | | /* 2. interval (lx,ux) halving |
78 | | regula falsi failed on qt(0.1, 0.1) |
79 | | */ |
80 | 0 | do { |
81 | 0 | nx = 0.5 * (lx + ux); |
82 | 0 | if (pt(nx, ndf, TRUE, FALSE) > p) ux = nx; else lx = nx; |
83 | 0 | } while ((ux - lx) / fabs(nx) > accu && ++iter < 1000); |
84 | |
|
85 | 0 | if(iter >= 1000) ML_WARNING(ME_PRECISION, "qt"); |
86 | |
|
87 | 0 | return 0.5 * (lx + ux); |
88 | 0 | } |
89 | | |
90 | | /* Old comment: |
91 | | * FIXME: "This test should depend on ndf AND p !! |
92 | | * ----- and in fact should be replaced by |
93 | | * something like Abramowitz & Stegun 26.7.5 (p.949)" |
94 | | * |
95 | | * That would say that if the qnorm value is x then |
96 | | * the result is about x + (x^3+x)/4df + (5x^5+16x^3+3x)/96df^2 |
97 | | * The differences are tiny even if x ~ 1e5, and qnorm is not |
98 | | * that accurate in the extreme tails. |
99 | | */ |
100 | 0 | if (ndf > 1e20) return qnorm(p, 0., 1., lower_tail, log_p); |
101 | | |
102 | 0 | P = R_D_qIv(p); /* if exp(p) underflows, we fix below */ |
103 | |
|
104 | 0 | bool neg = (!lower_tail || P < 0.5) && (lower_tail || P > 0.5), |
105 | 0 | is_neg_lower = (lower_tail == neg); /* both TRUE or FALSE == !xor */ |
106 | 0 | if(neg) |
107 | 0 | P = 2 * (log_p ? (lower_tail ? P : -expm1(p)) : R_D_Lval(p)); |
108 | 0 | else |
109 | 0 | P = 2 * (log_p ? (lower_tail ? -expm1(p) : P) : R_D_Cval(p)); |
110 | | /* 0 <= P <= 1 ; P = 2*min(P', 1 - P') in all cases */ |
111 | |
|
112 | 0 | if (fabs(ndf - 2) < eps) { /* df ~= 2 */ |
113 | 0 | if(P > DBL_MIN) { |
114 | 0 | if(3* P < DBL_EPSILON) /* P ~= 0 */ |
115 | 0 | q = 1 / sqrt(P); |
116 | 0 | else if (P > 0.9) /* P ~= 1 */ |
117 | 0 | q = (1 - P) * sqrt(2 /(P * (2 - P))); |
118 | 0 | else /* eps/3 <= P <= 0.9 */ |
119 | 0 | q = sqrt(2 / (P * (2 - P)) - 2); |
120 | 0 | } |
121 | 0 | else { /* P << 1, q = 1/sqrt(P) = ... */ |
122 | 0 | if(log_p) |
123 | 0 | q = is_neg_lower ? exp(- p/2) / M_SQRT2 : 1/sqrt(-expm1(p)); |
124 | 0 | else |
125 | 0 | q = ML_POSINF; |
126 | 0 | } |
127 | 0 | } |
128 | 0 | else if (ndf < 1 + eps) { /* df ~= 1 (df < 1 excluded above): Cauchy */ |
129 | 0 | if(P == 1.) q = 0; // some versions of tanpi give Inf, some NaN |
130 | 0 | else if(P > 0) |
131 | 0 | q = 1/tanpi(P/2.);/* == - tan((P+1) * M_PI_2) -- suffers for P ~= 0 */ |
132 | | |
133 | 0 | else { /* P = 0, but maybe = 2*exp(p) ! */ |
134 | 0 | if(log_p) /* 1/tan(e) ~ 1/e */ |
135 | 0 | q = is_neg_lower ? M_1_PI * exp(-p) : -1./(M_PI * expm1(p)); |
136 | 0 | else |
137 | 0 | q = ML_POSINF; |
138 | 0 | } |
139 | 0 | } |
140 | 0 | else { /*-- usual case; including, e.g., df = 1.1 */ |
141 | 0 | double x = 0., y, log_P2 = 0./* -Wall */, |
142 | 0 | a = 1 / (ndf - 0.5), |
143 | 0 | b = 48 / (a * a), |
144 | 0 | c = ((20700 * a / b - 98) * a - 16) * a + 96.36, |
145 | 0 | d = ((94.5 / (b + c) - 3) / b + 1) * sqrt(a * M_PI_2) * ndf; |
146 | 0 | bool |
147 | 0 | P_ok1 = P > DBL_MIN || !log_p, |
148 | 0 | P_ok = P_ok1; // when true (after check below), use "normal scale": log_p=FALSE |
149 | 0 | if(P_ok1) { |
150 | 0 | y = pow(d * P, 2.0 / ndf); |
151 | 0 | P_ok = (y >= DBL_EPSILON); |
152 | 0 | } |
153 | 0 | if(!P_ok) {// log.p && P very.small || (d*P)^(2/df) =: y < eps_c |
154 | 0 | log_P2 = is_neg_lower ? R_D_log(p) : R_D_LExp(p); /* == log(P / 2) */ |
155 | 0 | x = (log(d) + M_LN2 + log_P2) / ndf; |
156 | 0 | y = exp(2 * x); |
157 | 0 | } |
158 | |
|
159 | 0 | if ((ndf < 2.1 && P > 0.5) || y > 0.05 + a) { /* P > P0(df) */ |
160 | | /* Asymptotic inverse expansion about normal */ |
161 | 0 | if(P_ok) |
162 | 0 | x = qnorm(0.5 * P, 0., 1., /*lower_tail*/TRUE, /*log_p*/FALSE); |
163 | 0 | else /* log_p && P underflowed */ |
164 | 0 | x = qnorm(log_P2, 0., 1., lower_tail, /*log_p*/ TRUE); |
165 | |
|
166 | 0 | y = x * x; |
167 | 0 | if (ndf < 5) |
168 | 0 | c += 0.3 * (ndf - 4.5) * (x + 0.6); |
169 | 0 | c = (((0.05 * d * x - 5) * x - 7) * x - 2) * x + b + c; |
170 | 0 | y = (((((0.4 * y + 6.3) * y + 36) * y + 94.5) / c |
171 | 0 | - y - 3) / b + 1) * x; |
172 | 0 | y = expm1(a * y * y); |
173 | 0 | q = sqrt(ndf * y); |
174 | 0 | } else if(!P_ok && x < - M_LN2 * DBL_MANT_DIG) {/* 0.5* log(DBL_EPSILON) */ |
175 | | /* y above might have underflown */ |
176 | 0 | q = sqrt(ndf) * exp(-x); |
177 | 0 | } |
178 | 0 | else { /* re-use 'y' from above */ |
179 | 0 | y = ((1 / (((ndf + 6) / (ndf * y) - 0.089 * d - 0.822) |
180 | 0 | * (ndf + 2) * 3) + 0.5 / (ndf + 4)) |
181 | 0 | * y - 1) * (ndf + 1) / (ndf + 2) + 1 / y; |
182 | 0 | q = sqrt(ndf * y); |
183 | 0 | } |
184 | | |
185 | | |
186 | | /* Now apply 2-term Taylor expansion improvement (1-term = Newton): |
187 | | * as by Hill (1981) [ref.above] */ |
188 | | |
189 | | /* FIXME: This can be far from optimal when log_p = TRUE |
190 | | * but is still needed, e.g. for qt(-2, df=1.01, log=TRUE). |
191 | | * Probably also improvable when lower_tail = FALSE */ |
192 | |
|
193 | 0 | if(P_ok1) { |
194 | 0 | double M = fabs(sqrt(DBL_MAX/2.) - ndf); |
195 | 0 | int it=0; |
196 | 0 | while(it++ < 10 && (y = dt(q, ndf, FALSE)) > 0 && |
197 | 0 | R_FINITE(x = (pt(q, ndf, FALSE, FALSE) - P/2) / y) && |
198 | 0 | fabs(x) > 1e-14*fabs(q)) { |
199 | | /* Newton (=Taylor 1 term): |
200 | | * q += x; |
201 | | * Taylor 2-term : */ |
202 | 0 | double F = (fabs(q) < M) ? |
203 | 0 | q * (ndf + 1) / (2 * (q * q + ndf)) : |
204 | 0 | (ndf + 1) / (2 * (q + ndf/q)), |
205 | 0 | del_q = x * (1. + x * F); |
206 | 0 | if(R_FINITE(del_q) && R_FINITE(q + del_q)) |
207 | 0 | q += del_q; |
208 | 0 | else if(R_FINITE(x) && R_FINITE(q + x)) |
209 | 0 | q += x; |
210 | 0 | else // FIXME?? if q+x = +/-Inf is *better* than q should use it |
211 | 0 | break; // cannot improve q with a Newton/Taylor step |
212 | 0 | } |
213 | 0 | } |
214 | 0 | } |
215 | 0 | return neg ? -q : q; |
216 | 0 | } |