Coverage Report

Created: 2025-10-28 08:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pxfm-0.1.25/src/err/inverff.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 8/2025. All rights reserved.
3
 * //
4
 * // Redistribution and use in source and binary forms, with or without modification,
5
 * // are permitted provided that the following conditions are met:
6
 * //
7
 * // 1.  Redistributions of source code must retain the above copyright notice, this
8
 * // list of conditions and the following disclaimer.
9
 * //
10
 * // 2.  Redistributions in binary form must reproduce the above copyright notice,
11
 * // this list of conditions and the following disclaimer in the documentation
12
 * // and/or other materials provided with the distribution.
13
 * //
14
 * // 3.  Neither the name of the copyright holder nor the names of its
15
 * // contributors may be used to endorse or promote products derived from
16
 * // this software without specific prior written permission.
17
 * //
18
 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29
use crate::logs::simple_fast_log;
30
use crate::polyeval::{
31
    f_estrin_polyeval7, f_estrin_polyeval8, f_estrin_polyeval9, f_polyeval3, f_polyeval5,
32
    f_polyeval10, f_polyeval11,
33
};
34
35
#[inline]
36
0
pub(crate) fn erfinv_core(z: f64, ax: u32, sign: f32) -> f32 {
37
0
    if ax <= 0x3c1ba5e3u32 {
38
        // 0.0095
39
        // for small |x| using taylor series first 3 terms
40
0
        let z2 = z * z;
41
        // Generated by SageMath:
42
        // from mpmath import mp, erf
43
        //
44
        // mp.prec = 100
45
        //
46
        // def inverf_series(n_terms):
47
        //     from mpmath import taylor
48
        //     series_erf = taylor(mp.erfinv, 0, n_terms)
49
        //     return series_erf
50
        //
51
        // ser = inverf_series(10)
52
        // for i in range(1, len(ser), 2):
53
        //     k = ser[i]
54
        //     print("f64::from_bits(" + double_to_hex(RealField(100)(k)) + "),")
55
0
        let p = f_polyeval3(
56
0
            z2,
57
0
            f64::from_bits(0x3fec5bf891b4ef6b),
58
0
            f64::from_bits(0x3fcdb29fb2fee5e4),
59
0
            f64::from_bits(0x3fc053c2c0ab91c5),
60
0
        ) * z;
61
0
        return f32::copysign(p as f32, sign);
62
0
    } else if ax <= 0x3d75c28fu32 {
63
        // 0.06
64
        // for |x| < 0.06 using taylor series first 5 terms
65
0
        let z2 = z * z;
66
        // Generated by SageMath:
67
        // from mpmath import mp, erf
68
        //
69
        // mp.prec = 100
70
        //
71
        // def inverf_series(n_terms):
72
        //     from mpmath import taylor
73
        //     series_erf = taylor(mp.erfinv, 0, n_terms)
74
        //     return series_erf
75
        //
76
        // ser = inverf_series(10)
77
        // for i in range(1, len(ser), 2):
78
        //     k = ser[i]
79
        //     print("f64::from_bits(" + double_to_hex(RealField(100)(k)) + "),")
80
0
        let p = f_polyeval5(
81
0
            z2,
82
0
            f64::from_bits(0x3fec5bf891b4ef6b),
83
0
            f64::from_bits(0x3fcdb29fb2fee5e4),
84
0
            f64::from_bits(0x3fc053c2c0ab91c5),
85
0
            f64::from_bits(0x3fb62847c47dda48),
86
0
            f64::from_bits(0x3fb0a13189c6ef7a),
87
0
        ) * z;
88
0
        return f32::copysign(p as f32, sign);
89
0
    }
90
91
0
    if ax <= 0x3f400000u32 {
92
        // |x| <= 0.75
93
0
        let z2 = z * z;
94
95
        // First step rational approximant is generated, but it's ill-conditioned, thus
96
        // we're using taylor expansion to create Newton form at the point.
97
        //
98
        // <<FunctionApproximations`
99
        // ClearAll["Global`*"]
100
        // f[x_]:=InverseErf[Sqrt[x]]/Sqrt[x]
101
        // {err0, approx}=MiniMaxApproximation[f[z],{z,{0.06,0.75},8,7},WorkingPrecision->70]
102
        // num=Numerator[approx][[1]];
103
        // den=Denominator[approx][[1]];
104
        // poly=num;
105
        // coeffs=CoefficientList[poly,z];
106
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
107
0
        let r = z2 - 0.5625;
108
        // x0=SetPrecision[0.5625,75];
109
        // NumberForm[Series[num[x],{x,x0,50}], ExponentFunction->(Null&)]
110
        // coeffs=Table[SeriesCoefficient[num[x],{x,x0,k}],{k,0,8}];
111
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
112
0
        let p_num = f_estrin_polyeval9(
113
0
            r,
114
0
            f64::from_bits(0x3fa329348a73d9d4),
115
0
            f64::from_bits(0xbfd2cb089b644580),
116
0
            f64::from_bits(0x3fed229149f732d6),
117
0
            f64::from_bits(0xbff6a233d2028bff),
118
0
            f64::from_bits(0x3ff268adbfbb6023),
119
0
            f64::from_bits(0xbfddac401c7d70f4),
120
0
            f64::from_bits(0x3fb3b1bd759d5046),
121
0
            f64::from_bits(0xbf67aeb45bad547e),
122
0
            f64::from_bits(0xbf01ccc7434d381b),
123
        );
124
        // x0=SetPrecision[0.5625,75];
125
        // NumberForm[Series[den[x],{x,x0,50}], ExponentFunction->(Null&)]
126
        // coeffs=Table[SeriesCoefficient[den[x],{x,x0,k}],{k,0,7}];
127
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
128
0
        let p_den = f_estrin_polyeval8(
129
0
            r,
130
0
            f64::from_bits(0x3fa1aac2ee4b1413),
131
0
            f64::from_bits(0xbfd279342e281c99),
132
0
            f64::from_bits(0x3feef89a353c6d1b),
133
0
            f64::from_bits(0xbffa8f1b7cd6d0a7),
134
0
            f64::from_bits(0x3ff89ce6289819a1),
135
0
            f64::from_bits(0xbfe7db5282a4a2e1),
136
0
            f64::from_bits(0x3fc543f9a928db4a),
137
0
            f64::from_bits(0xbf888fd2990e88db),
138
        );
139
0
        let k = (p_num / p_den) * z;
140
0
        f32::copysign(k as f32, sign)
141
0
    } else if ax <= 0x3f580000u32 {
142
        // |x| <= 0.84375
143
0
        let z2 = z * z;
144
145
        // First step rational approximant is generated, but it's ill-conditioned, thus
146
        // we're using taylor expansion to create Newton form at the point.
147
        //
148
        // <<FunctionApproximations`
149
        // ClearAll["Global`*"]
150
        // f[x_]:=InverseErf[Sqrt[x]]/Sqrt[x]
151
        // {err0, approx}=MiniMaxApproximation[f[z],{z,{0.75,0.84375},6,6},WorkingPrecision->70]
152
        // num=Numerator[approx][[1]];
153
        // den=Denominator[approx][[1]];
154
        // poly=num;
155
        // coeffs=CoefficientList[poly,z];
156
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
157
0
        let r = z2 - 0.84375;
158
        // x0=SetPrecision[0.84375,75];
159
        // NumberForm[Series[num[x],{x,x0,50}], ExponentFunction->(Null&)]
160
        // coeffs=Table[SeriesCoefficient[num[x],{x,x0,k}],{k,0,9}];
161
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
162
0
        let p_num = f_polyeval10(
163
0
            r,
164
0
            f64::from_bits(0x3f116d07e62cbb74),
165
0
            f64::from_bits(0xbf5c38d390052412),
166
0
            f64::from_bits(0x3f92d6f96f84efe3),
167
0
            f64::from_bits(0xbfbac9189cae446b),
168
0
            f64::from_bits(0x3fd5dd124fb25677),
169
0
            f64::from_bits(0xbfe49845d46b80ab),
170
0
            f64::from_bits(0x3fe556c4913f60f8),
171
0
            f64::from_bits(0xbfd59e527704e33b),
172
0
            f64::from_bits(0x3fb07614a5e6c9f1),
173
0
            f64::from_bits(0xbf60ce54a2d8a789),
174
        );
175
        // x0=SetPrecision[0.84375,75];
176
        // NumberForm[Series[den[x],{x,x0,50}], ExponentFunction->(Null&)]
177
        // coeffs=Table[SeriesCoefficient[den[x],{x,x0,k}],{k,0,9}];
178
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
179
0
        let p_den = f_polyeval10(
180
0
            r,
181
0
            f64::from_bits(0x3f09fbdd1c987d1e),
182
0
            f64::from_bits(0xbf5602ad17d419f4),
183
0
            f64::from_bits(0x3f8efe31ea5bc71d),
184
0
            f64::from_bits(0xbfb77e5f1bd26730),
185
0
            f64::from_bits(0x3fd4c3f03e4f5478),
186
0
            f64::from_bits(0xbfe5aa87dfc5e757),
187
0
            f64::from_bits(0x3fe9c6406f9abc0b),
188
0
            f64::from_bits(0xbfdff2f008b4db05),
189
0
            f64::from_bits(0x3fc1123be5319800),
190
0
            f64::from_bits(0xbf83be49c2d5cb9e),
191
        );
192
0
        let k = (p_num / p_den) * z;
193
0
        f32::copysign(k as f32, sign)
194
0
    } else if ax <= 0x3f700000u32 {
195
        // |x| <= 0.9375
196
        // First step rational approximant is generated, but it's ill-conditioned, thus
197
        // we're using taylor expansion to create Newton form at the point.
198
        //
199
        // <<FunctionApproximations`
200
        // ClearAll["Global`*"]
201
        // f[x_]:=InverseErf[Sqrt[x]]/Sqrt[x]
202
        // {err0, approx}=MiniMaxApproximation[f[z],{z,{0.84375,0.9375},10,9},WorkingPrecision->70]
203
        // num=Numerator[approx][[1]];
204
        // den=Denominator[approx][[1]];
205
        // coeffs=CoefficientList[poly,z];
206
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
207
0
        let x2 = z * z;
208
0
        let r = x2 - 0.87890625;
209
        // x0=SetPrecision[0.87890625,75];
210
        // NumberForm[Series[num[x],{x,x0,50}], ExponentFunction->(Null&)]
211
        // coeffs=Table[SeriesCoefficient[num[x],{x,x0,k}],{k,0,9}];
212
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
213
0
        let p_num = f_polyeval11(
214
0
            r,
215
0
            f64::from_bits(0x3ec70f1cbf8a758b),
216
0
            f64::from_bits(0xbf1c9dff87b698d0),
217
0
            f64::from_bits(0x3f5dfe7be00cc21c),
218
0
            f64::from_bits(0xbf913fd09c5a3682),
219
0
            f64::from_bits(0x3fb7ab0095693976),
220
0
            f64::from_bits(0xbfd3b3ca6a3c9919),
221
0
            f64::from_bits(0x3fe3533be6d1d8c8),
222
0
            f64::from_bits(0xbfe48208ef308ac7),
223
0
            f64::from_bits(0x3fd361a82dab69d1),
224
0
            f64::from_bits(0xbfa2401965a98195),
225
0
            f64::from_bits(0xbf54ba4d14ca54e3),
226
        );
227
        // x0=SetPrecision[0.87890625,75];
228
        // NumberForm[Series[den[x],{x,x0,50}], ExponentFunction->(Null&)]
229
        // coeffs=Table[SeriesCoefficient[den[x],{x,x0,k}],{k,0,9}];
230
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
231
0
        let p_den = f_polyeval10(
232
0
            r,
233
0
            f64::from_bits(0x3ec0699f391e2327),
234
0
            f64::from_bits(0xbf151ec184941078),
235
0
            f64::from_bits(0x3f5717bb379a3c6e),
236
0
            f64::from_bits(0xbf8beed3755c3484),
237
0
            f64::from_bits(0x3fb46148b4a431ef),
238
0
            f64::from_bits(0xbfd25690b7bc76fa),
239
0
            f64::from_bits(0x3fe3f1b2f4ee0d9d),
240
0
            f64::from_bits(0xbfe888a7a4511975),
241
0
            f64::from_bits(0x3fdd84db18f2a240),
242
0
            f64::from_bits(0xbfb844807521be56),
243
        );
244
0
        let f = z * (p_num / p_den);
245
0
        f32::copysign(f as f32, sign)
246
    } else {
247
        // Rational approximation generated by Wolfram Mathematica:
248
        // for inverf(x) = sqrt(-log(1-x))*R(1/sqrt(-log(1-x)))
249
        //
250
        // <<FunctionApproximations`
251
        // ClearAll["Global`*"]
252
        // f[x_]:=InverseErf[Exp[-1/(x^2)]*(-1+Exp[1/(x^2)])]/(Sqrt[-Log[1-(Exp[-1/(x^2)]*(-1+Exp[1/(x^2)]))]] )
253
        // {err0, approx,err1}=MiniMaxApproximation[f[z],{z,{0.2,0.9999999},7,6},WorkingPrecision->90]
254
        // num=Numerator[approx];
255
        // den=Denominator[approx];
256
        // poly=num;
257
        // coeffs=CoefficientList[poly,z];
258
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
259
        // poly=den;
260
        // coeffs=CoefficientList[poly,z];
261
        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
262
0
        let zeta = -simple_fast_log(1. - z);
263
0
        let zeta_sqrt = zeta.sqrt();
264
0
        let rcp_zeta = (1. / zeta) * zeta_sqrt;
265
0
        let p_num = f_estrin_polyeval8(
266
0
            rcp_zeta,
267
0
            f64::from_bits(0x3ff00072876c578e),
268
0
            f64::from_bits(0x40314e00c10282da),
269
0
            f64::from_bits(0x404f4a1412af03f6),
270
0
            f64::from_bits(0x404c895cc0d9b1b3),
271
0
            f64::from_bits(0x404545794620bfaf),
272
0
            f64::from_bits(0x403264d21ea21354),
273
0
            f64::from_bits(0x3fc5a5141dd19237),
274
0
            f64::from_bits(0xbf8c2e49707c21ec),
275
        );
276
0
        let p_den = f_estrin_polyeval7(
277
0
            rcp_zeta,
278
0
            f64::from_bits(0x3ff0000000000000),
279
0
            f64::from_bits(0x403151312c313d77),
280
0
            f64::from_bits(0x405032345fa3d0cd),
281
0
            f64::from_bits(0x4053e0a81d4c5f09),
282
0
            f64::from_bits(0x4054fa20c5e0731c),
283
0
            f64::from_bits(0x404620d7f94d4804),
284
0
            f64::from_bits(0x4035d7400867b81f),
285
        );
286
0
        let r = zeta_sqrt * (p_num / p_den);
287
0
        f32::copysign(r as f32, sign)
288
    }
289
0
}
290
291
/// Inverse error function
292
///
293
/// Max ulp 0.5
294
0
pub fn f_erfinvf(x: f32) -> f32 {
295
0
    let ax = x.to_bits() & 0x7fff_ffff;
296
297
0
    if ax >= 0x3f800000u32 || ax <= 0x3400_0000u32 {
298
        // |x| >= 1, |x| == 0, |x| <= f32::EPSILON
299
0
        if ax == 0 {
300
            // |x| == 0
301
0
            return 0.;
302
0
        }
303
304
0
        if ax <= 0x3400_0000u32 {
305
            // |x| <= f32::EPSILON
306
            // inverf(x) ~ Sqrt[Pi]x/2+O[x]^3
307
            const SQRT_PI_OVER_2: f64 = f64::from_bits(0x3fec5bf891b4ef6b);
308
0
            return (x as f64 * SQRT_PI_OVER_2) as f32;
309
0
        }
310
311
0
        if ax == 0x3f800000u32 {
312
            // |x| == 1
313
0
            return if x.is_sign_negative() {
314
0
                f32::NEG_INFINITY
315
            } else {
316
0
                f32::INFINITY
317
            };
318
0
        }
319
320
        // |x| > 1
321
0
        return f32::NAN; // |x| == NaN, |x| == Inf, |x| > 1
322
0
    }
323
324
0
    let z = f32::from_bits(ax) as f64;
325
0
    erfinv_core(z, ax, x)
326
0
}
327
328
#[cfg(test)]
329
mod tests {
330
    use super::*;
331
332
    #[test]
333
    fn f_test_inv_erff() {
334
        assert!(f_erfinvf(f32::NEG_INFINITY).is_nan());
335
        assert!(f_erfinvf(f32::INFINITY).is_nan());
336
        assert!(f_erfinvf(-1.1).is_nan());
337
        assert!(f_erfinvf(1.1).is_nan());
338
        assert_eq!(f_erfinvf(f32::EPSILON), 1.05646485e-7);
339
        assert_eq!(f_erfinvf(-1.), f32::NEG_INFINITY);
340
        assert_eq!(f_erfinvf(1.), f32::INFINITY);
341
        assert_eq!(f_erfinvf(0.002), 0.0017724558);
342
        assert_eq!(f_erfinvf(-0.002), -0.0017724558);
343
        assert_eq!(f_erfinvf(0.02), 0.017726395);
344
        assert_eq!(f_erfinvf(-0.02), -0.017726395);
345
        assert_eq!(f_erfinvf(0.05), 0.044340387);
346
        assert_eq!(f_erfinvf(-0.05), -0.044340387);
347
        assert_eq!(f_erfinvf(0.5), 0.47693628);
348
        assert_eq!(f_erfinvf(-0.5), -0.47693628);
349
        assert_eq!(f_erfinvf(0.76), 0.8308411);
350
        assert_eq!(f_erfinvf(-0.76), -0.8308411);
351
        assert_eq!(f_erfinvf(0.92), 1.2379221);
352
        assert_eq!(f_erfinvf(-0.92), -1.2379221);
353
        assert_eq!(f_erfinvf(0.97), 1.5344859);
354
        assert_eq!(f_erfinvf(-0.97), -1.5344859);
355
        assert_eq!(f_erfinvf(0.99), 1.8213866);
356
        assert_eq!(f_erfinvf(-0.99), -1.8213866);
357
        assert_eq!(f_erfinvf(0.7560265), 0.82385886);
358
    }
359
}