pxfm/bessel/i2f.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::bessel::j0f::j1f_rsqrt;
30use crate::exponents::core_expf;
31use crate::polyeval::{f_estrin_polyeval8, f_estrin_polyeval9};
32
33/// Modified Bessel of the first kind of order 2
34///
35/// ULP 0.5
36pub fn f_i2f(x: f32) -> f32 {
37 let ux = x.to_bits().wrapping_shl(1);
38 if ux >= 0xffu32 << 24 || ux == 0 {
39 // |x| == 0, |x| == inf, |x| == NaN
40 if ux == 0 {
41 // |x| == 0
42 return 0.;
43 }
44 if x.is_infinite() {
45 return f32::INFINITY;
46 }
47 return x + f32::NAN; // x == NaN
48 }
49
50 let xb = x.to_bits() & 0x7fff_ffff;
51
52 if xb >= 0x42b7d875u32 {
53 // |x| >= 91.92277 it's infinity
54 return f32::INFINITY;
55 }
56
57 if xb <= 0x40f80000u32 {
58 // |x| <= 7.75
59 if xb <= 0x34000000u32 {
60 // |x| <= f32::EPSILON
61 let dx = x as f64;
62 const R: f64 = 1. / 8.;
63 return (dx * dx * R) as f32;
64 }
65 return i2f_small(f32::from_bits(xb));
66 }
67
68 i2f_asympt(f32::from_bits(xb))
69}
70
71/**
72Computes
73I2(x) = x^2 * R(x^2)
74
75Generated by Wolfram Mathematica:
76
77```text
78<<FunctionApproximations`
79ClearAll["Global`*"]
80f[x_]:=BesselI[2,x]/x^2
81g[z_]:=f[Sqrt[z]]
82{err,approx}=MiniMaxApproximation[g[z],{z,{0.000000000001,7.75},8,7},WorkingPrecision->75]
83poly=Numerator[approx][[1]];
84coeffs=CoefficientList[poly,z];
85TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
86poly=Denominator[approx][[1]];
87coeffs=CoefficientList[poly,z];
88TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
89```
90**/
91#[inline]
92fn i2f_small(x: f32) -> f32 {
93 let dx = x as f64;
94 let x_sqr = dx * dx;
95
96 let p_num = f_estrin_polyeval9(
97 x_sqr,
98 f64::from_bits(0x3fc0000000000000),
99 f64::from_bits(0x3f831469a38d72c7),
100 f64::from_bits(0x3f2f453dd3dd98f4),
101 f64::from_bits(0x3ec8af52ee8fce9b),
102 f64::from_bits(0x3e5589f2cb4e0ec9),
103 f64::from_bits(0x3dd60fa268a4206d),
104 f64::from_bits(0x3d4ab3091ee18d6b),
105 f64::from_bits(0x3cb1efec43b15186),
106 f64::from_bits(0x3c050992c6e9e63f),
107 );
108 let p_den = f_estrin_polyeval8(
109 x_sqr,
110 f64::from_bits(0x3ff0000000000000),
111 f64::from_bits(0xbf82075d8e3f1476),
112 f64::from_bits(0x3f03ef86564a284b),
113 f64::from_bits(0xbe7c498fab4a57d8),
114 f64::from_bits(0x3dec162ca0f68486),
115 f64::from_bits(0xbd53bb6398461540),
116 f64::from_bits(0x3cb265215261e64a),
117 f64::from_bits(0xbc01cf52cc350e81),
118 );
119 let p = p_num / p_den;
120 (p * x_sqr) as f32
121}
122
123/**
124Asymptotic expansion for I2.
125
126Computes:
127sqrt(x) * exp(-x) * I2(x) = Pn(1/x)/Qn(1/x)
128hence:
129I2(x) = Pn(1/x)/Qm(1/x)*exp(x)/sqrt(x)
130
131Generated by Mathematica:
132```text
133<<FunctionApproximations`
134ClearAll["Global`*"]
135f[x_]:=Sqrt[x] Exp[-x] BesselI[2,x]
136g[z_]:=f[1/z]
137{err,approx}=MiniMaxApproximation[g[z],{z,{1/92.3,1/7.5},8,8},WorkingPrecision->70]
138poly=Numerator[approx][[1]];
139coeffs=CoefficientList[poly,z];
140TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
141poly=Denominator[approx][[1]];
142coeffs=CoefficientList[poly,z];
143TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
144```
145**/
146#[inline]
147fn i2f_asympt(x: f32) -> f32 {
148 let dx = x as f64;
149 let recip = 1. / dx;
150 let p_num = f_estrin_polyeval9(
151 recip,
152 f64::from_bits(0x3fd9884533d45f46),
153 f64::from_bits(0xc02b979526807e1e),
154 f64::from_bits(0x406b1dd3e795bbed),
155 f64::from_bits(0xc09e43629031ec91),
156 f64::from_bits(0x40c48c03a39aec1d),
157 f64::from_bits(0xc0e0f022ccb8807a),
158 f64::from_bits(0x40f0302eeb22a776),
159 f64::from_bits(0xc0f02b01549d38b8),
160 f64::from_bits(0x40dad4e70f2bc264),
161 );
162 let p_den = f_estrin_polyeval9(
163 recip,
164 f64::from_bits(0x3ff0000000000000),
165 f64::from_bits(0xc0405a71a88b191c),
166 f64::from_bits(0x407e19f7d247d098),
167 f64::from_bits(0xc0aeaac6e0ca17fe),
168 f64::from_bits(0x40d2301702f40a98),
169 f64::from_bits(0xc0e7e6c6c01841b3),
170 f64::from_bits(0x40ed67317e9e46cc),
171 f64::from_bits(0xc0d13786aa1ef416),
172 f64::from_bits(0xc0a6c9cfe579ae22),
173 );
174 let z = p_num / p_den;
175
176 let e = core_expf(x);
177 let r_sqrt = j1f_rsqrt(dx);
178 (z * r_sqrt * e) as f32
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 #[test]
185 fn test_i2f() {
186 assert_eq!(f_i2f(0.), 0.);
187 assert_eq!(f_i2f(f32::INFINITY), f32::INFINITY);
188 assert_eq!(f_i2f(f32::NEG_INFINITY), f32::INFINITY);
189 assert_eq!(f_i2f(1.), 0.13574767);
190 assert_eq!(f_i2f(-1.), 0.13574767);
191 assert_eq!(f_i2f(9.432), 1314.6553);
192 assert_eq!(f_i2f(-9.432), 1314.6553);
193 }
194}