pxfm/bessel/
k2f.rs

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 */
29use crate::common::f_fmla;
30use crate::exponents::core_expf;
31use crate::logs::fast_logf;
32use crate::polyeval::{f_estrin_polyeval5, f_estrin_polyeval8, f_polyeval4, f_polyeval11};
33
34/// Modified Bessel of the second kind of order 2
35///
36/// ulp 0.5
37pub fn f_k2f(x: f32) -> f32 {
38    let ux = x.to_bits();
39    if ux >= 0xffu32 << 23 || ux == 0 {
40        // |x| == 0, |x| == inf, |x| == NaN, x < 0
41        if ux.wrapping_shl(1) == 0 {
42            // |x| == 0
43            return f32::INFINITY;
44        }
45        if x.is_infinite() {
46            return if x.is_sign_positive() { 0. } else { f32::NAN };
47        }
48        return x + f32::NAN; // x == NaN
49    }
50
51    let xb = x.to_bits();
52
53    if xb >= 0x42cbceefu32 {
54        // |x| >= 101.90417
55        return 0.;
56    }
57
58    if xb <= 0x3f800000u32 {
59        // x <= 1.0
60        if xb <= 0x3e9eb852u32 {
61            // x <= 0.31
62            if xb <= 0x34000000u32 {
63                // x <= f32::EPSILON
64                let dx = x as f64;
65                let r = 2. / (dx * dx);
66                return r as f32;
67            }
68            return k2f_tiny(x);
69        }
70        return k2f_small(x);
71    }
72
73    k2f_asympt(x)
74}
75
76#[inline]
77fn k2f_tiny(x: f32) -> f32 {
78    // Power series at zero for K2
79    // 2.0000000000000000/x^2-0.50000000000000000-0.12500000000000000 (-0.86593151565841245+1.0000000000000000 Log[x]) x^2-0.010416666666666667 (-1.5325981823250791+1.0000000000000000 Log[x]) x^4-0.00032552083333333333 (-1.9075981823250791+1.0000000000000000 Log[x]) x^6-0.0000054253472222222222 (-2.1742648489917458+1.0000000000000000 Log[x]) x^8+O[x]^9
80    //-0.50000000000000000+2.0000000000000000/x^2 + a3 * x^8 + x^6 * a2 + x^4 * a1 + x^2 * a0
81    let dx = x as f64;
82    let log_x = fast_logf(x);
83    let a0 = f_fmla(-4.0000000000000000, log_x, 3.4637260626336498) * 0.031250000000000000;
84    let a1 = f_fmla(-12.000000000000000, log_x, 18.391178187900949) * 0.00086805555555555556;
85    let a2 = f_fmla(-24.000000000000000, log_x, 45.782356375801899) * 0.000013563368055555556;
86    let a3 = (log_x - 2.1742648489917458) * (-0.0000054253472222222222);
87    let dx_sqr = dx * dx;
88    let two_over_dx = 2. / dx_sqr;
89    let p = f_polyeval4(dx_sqr, a0, a1, a2, a3);
90    let r = f_fmla(p, dx_sqr, two_over_dx) - 0.5;
91    r as f32
92}
93
94/**
95Computes
96I2(x) = x^2 * R(x^2)
97
98Generated by Wolfram Mathematica:
99
100```text
101<<FunctionApproximations`
102ClearAll["Global`*"]
103f[x_]:=BesselI[2,x]/x^2
104g[z_]:=f[Sqrt[z]]
105{err,approx}=MiniMaxApproximation[g[z],{z,{0.3,1},4,4},WorkingPrecision->75]
106poly=Numerator[approx][[1]];
107coeffs=CoefficientList[poly,z];
108TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
109poly=Denominator[approx][[1]];
110coeffs=CoefficientList[poly,z];
111TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
112```
113**/
114#[inline]
115fn i2f_small(x: f32) -> f64 {
116    let dx = x as f64;
117    let x_sqr = dx * dx;
118
119    let p_num = f_estrin_polyeval5(
120        x_sqr,
121        f64::from_bits(0x3fc0000000000000),
122        f64::from_bits(0x3f81520c0669099e),
123        f64::from_bits(0x3f27310bf5c5e9b0),
124        f64::from_bits(0x3eb8e2947e0a6098),
125        f64::from_bits(0x3e336dfad46e2f35),
126    );
127    let p_den = f_estrin_polyeval5(
128        x_sqr,
129        f64::from_bits(0x3ff0000000000000),
130        f64::from_bits(0xbf900d253bb12edc),
131        f64::from_bits(0x3f1ed3d9ab228297),
132        f64::from_bits(0xbea14e6660c00303),
133        f64::from_bits(0x3e13eb951a6cf38f),
134    );
135    let p = p_num / p_den;
136    p * x_sqr
137}
138
139/**
140Series for
141R(x^2) := (BesselK(2, x) - Log(x)*BesselI(2, x) - 2/x^2)/(1+x^2)
142
143Generated by Wolfram Mathematica:
144```text
145<<FunctionApproximations`
146ClearAll["Global`*"]
147f[x_]:=(BesselK[2,x]-Log[x]BesselI[2,x]-2/(x^2))/(1+x^2)
148g[z_]:=f[Sqrt[z]]
149{err,approx}=MiniMaxApproximation[g[z],{z,{0.3,1.0},10,10},WorkingPrecision->60]
150poly=Numerator[approx][[1]];
151coeffs=CoefficientList[poly,z];
152TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
153poly=Denominator[approx][[1]];
154coeffs=CoefficientList[poly,z];
155TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
156```
157**/
158#[inline]
159fn k2f_small(x: f32) -> f32 {
160    let dx = x as f64;
161    let dx_sqr = dx * dx;
162    let p_num = f_polyeval11(
163        dx_sqr,
164        f64::from_bits(0xbfdff794c9ee3b5c),
165        f64::from_bits(0xc047d3276f18e5d2),
166        f64::from_bits(0xc09200ed3702875a),
167        f64::from_bits(0xc0c39f395c47be27),
168        f64::from_bits(0xc0e0ec95bd1a3192),
169        f64::from_bits(0xc0e5973cb871c8d0),
170        f64::from_bits(0xc0cdaf528de00d53),
171        f64::from_bits(0xc0afe6d3009de17c),
172        f64::from_bits(0xc098417b22844112),
173        f64::from_bits(0x4025c45260bb1b6a),
174        f64::from_bits(0x402f2bf6b95ffe0c),
175    );
176    let p_den = f_polyeval11(
177        dx_sqr,
178        f64::from_bits(0x3ff0000000000000),
179        f64::from_bits(0x405879a43b253224),
180        f64::from_bits(0x40a3a501408a0198),
181        f64::from_bits(0x40d8172abc4a8ccc),
182        f64::from_bits(0x40f9fcb05e98bdbd),
183        f64::from_bits(0x4109c45b54be586b),
184        f64::from_bits(0x4106ad7023dd0b90),
185        f64::from_bits(0x40ed7e988d2ba5a9),
186        f64::from_bits(0x40966305e1c1123a),
187        f64::from_bits(0xc090832b6a87317c),
188        f64::from_bits(0x403b48eb703f4644),
189    );
190    let p = p_num / p_den;
191
192    let two_over_dx_sqr = 2. / dx_sqr;
193
194    let lg = fast_logf(x);
195    let v_i = i2f_small(x);
196    let z = f_fmla(lg, v_i, two_over_dx_sqr);
197    let z0 = f_fmla(p, f_fmla(dx, dx, 1.), z);
198    z0 as f32
199}
200
201/**
202Generated by Wolfram Mathematica:
203```text
204<<FunctionApproximations`
205ClearAll["Global`*"]
206f[x_]:=Sqrt[x] Exp[x] BesselK[1,x]
207g[z_]:=f[1/z]
208{err, approx}=MiniMaxApproximation[g[z],{z,{0.000000001,1},7,7},WorkingPrecision->60]
209poly=Numerator[approx][[1]];
210coeffs=CoefficientList[poly,z];
211TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
212poly=Denominator[approx][[1]];
213coeffs=CoefficientList[poly,z];
214TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
215```
216**/
217#[inline]
218fn k2f_asympt(x: f32) -> f32 {
219    let dx = x as f64;
220    let recip = 1. / dx;
221    let e = core_expf(x);
222    let r_sqrt = dx.sqrt();
223    let p_num = f_estrin_polyeval8(
224        recip,
225        f64::from_bits(0x3ff40d931ff626f2),
226        f64::from_bits(0x402d954dceb445df),
227        f64::from_bits(0x405084ea6680d028),
228        f64::from_bits(0x406242344a8ea488),
229        f64::from_bits(0x406594aa56f50fea),
230        f64::from_bits(0x405aa04eb4f0af1c),
231        f64::from_bits(0x403dd3e8e63849ef),
232        f64::from_bits(0x4004e85453648d43),
233    );
234    let p_den = f_estrin_polyeval8(
235        recip,
236        f64::from_bits(0x3ff0000000000000),
237        f64::from_bits(0x4023da9f4e05358e),
238        f64::from_bits(0x4040a4e4ceb523c9),
239        f64::from_bits(0x404725c423c9f990),
240        f64::from_bits(0x403a60c00deededc),
241        f64::from_bits(0x40149975b84c3946),
242        f64::from_bits(0x3fc69439846db871),
243        f64::from_bits(0xbf6400819bac6f45),
244    );
245    let v = p_num / p_den;
246    let pp = v / (e * r_sqrt);
247    pp as f32
248}
249
250#[cfg(test)]
251mod tests {
252    use super::*;
253
254    #[test]
255    fn test_k2f() {
256        assert!(f_k2f(-1.).is_nan());
257        assert!(f_k2f(f32::NAN).is_nan());
258        assert_eq!(f_k2f(0.), f32::INFINITY);
259        assert_eq!(f_k2f(0.65), 4.3059196);
260        assert_eq!(f_k2f(1.65), 0.44830766);
261    }
262}