/* * // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved. * // * // Redistribution and use in source and binary forms, with or without modification, * // are permitted provided that the following conditions are met: * // * // 1. Redistributions of source code must retain the above copyright notice, this * // list of conditions and the following disclaimer. * // * // 2. Redistributions in binary form must reproduce the above copyright notice, * // this list of conditions and the following disclaimer in the documentation * // and/or other materials provided with the distribution. * // * // 3. Neither the name of the copyright holder nor the names of its * // contributors may be used to endorse or promote products derived from * // this software without specific prior written permission. * // * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ use crate::bessel::j0f::j1f_rsqrt; use crate::common::f_fmla; use crate::exponents::core_expf; use crate::logs::fast_logf; use crate::polyeval::{f_estrin_polyeval8, f_polyeval3, f_polyeval4}; /// Modified exponentially scaled Bessel of the second kind of order 1 /// /// Computes K1(x)exp(x) /// /// Max ULP 0.5 pub fn f_k1ef(x: f32) -> f32 { let ux = x.to_bits(); if ux >= 0xffu32 << 23 || ux == 0 { // |x| == 0, |x| == inf, |x| == NaN, x < 0 if ux.wrapping_shl(1) == 0 { return f32::INFINITY; } if x.is_infinite() { return if x.is_sign_positive() { 0. } else { f32::NAN }; } return x + f32::NAN; // x == NaN } let xb = x.to_bits(); if xb <= 0x3f800000u32 { // x <= 1.0 if xb <= 0x34000000u32 { // |x| <= f32::EPSILON let dx = x as f64; let leading_term = 1. / dx + 1.; if xb <= 0x3109705fu32 { // |x| <= 2e-9 // taylor series for tiny K1(x)exp(x) ~ 1/x + 1 + O(x) return leading_term as f32; } // taylor series for small K1(x)exp(x) ~ 1/x+1+1/4 (1+2 EulerGamma-2 Log[2]+2 Log[x]) x + O(x^3) const C: f64 = f64::from_bits(0xbffd8773039049e8); // 1 + 2 EulerGamma-2 Log[2] let log_x = fast_logf(x); let r = f_fmla(log_x, 2., C); let w0 = f_fmla(dx * 0.25, r, leading_term); return w0 as f32; } return k1ef_small(x); } k1ef_asympt(x) } /** Computes I1(x) = x/2 * (1 + 1 * (x/2)^2 + (x/2)^4 * P((x/2)^2)) Generated by Woflram Mathematica: ```text <60] poly=Numerator[approx][[1]]; coeffs=CoefficientList[poly,z]; TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] poly=Denominator[approx][[1]]; coeffs=CoefficientList[poly,z]; TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] ``` **/ #[inline] fn i1f_small(x: f32) -> f64 { let dx = x as f64; let x_over_two = dx * 0.5; let x_over_two_sqr = x_over_two * x_over_two; let x_over_two_p4 = x_over_two_sqr * x_over_two_sqr; let p_num = f_polyeval4( x_over_two_sqr, f64::from_bits(0x3fb5555555555355), f64::from_bits(0x3f6ebf07f0dbc49b), f64::from_bits(0x3f1fdc02bf28a8d9), f64::from_bits(0x3ebb5e7574c700a6), ); let p_den = f_polyeval3( x_over_two_sqr, f64::from_bits(0x3ff0000000000000), f64::from_bits(0xbfa39b64b6135b5a), f64::from_bits(0x3f3fa729bbe951f9), ); let p = p_num / p_den; let p1 = f_fmla(0.5, x_over_two_sqr, 1.); let p2 = f_fmla(x_over_two_p4, p, p1); p2 * x_over_two } /** Series for f(x) := BesselK(1, x) - Log(x)*BesselI(1, x) - 1/x Generated by Wolfram Mathematica: ```text <60] poly=Numerator[approx][[1]]; coeffs=CoefficientList[poly,z]; TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] poly=Denominator[approx][[1]]; coeffs=CoefficientList[poly,z]; TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] ``` **/ #[inline] fn k1ef_small(x: f32) -> f32 { let dx = x as f64; let rcp = 1. / dx; let x2 = dx * dx; let p_num = f_polyeval4( x2, f64::from_bits(0xbfd3b5b6028a83d6), f64::from_bits(0xbfb3fde2c83f7cca), f64::from_bits(0xbf662b2e5defbe8c), f64::from_bits(0xbefa2a63cc5c4feb), ); let p_den = f_polyeval4( x2, f64::from_bits(0x3ff0000000000000), f64::from_bits(0xbf9833197207a7c6), f64::from_bits(0x3f315663bc7330ef), f64::from_bits(0xbeb9211958f6b8c3), ); let p = p_num / p_den; let v_exp = core_expf(x); let lg = fast_logf(x); let v_i = i1f_small(x); let z = f_fmla(lg, v_i, rcp); let z0 = f_fmla(p, dx, z); (z0 * v_exp) as f32 } /** Generated by Wolfram Mathematica: ```text <60] poly=Numerator[approx][[1]]; coeffs=CoefficientList[poly,z]; TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] poly=Denominator[approx][[1]]; coeffs=CoefficientList[poly,z]; TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]] ``` **/ #[inline] fn k1ef_asympt(x: f32) -> f32 { let dx = x as f64; let recip = 1. / dx; let r_sqrt = j1f_rsqrt(dx); let p_num = f_estrin_polyeval8( recip, f64::from_bits(0x3ff40d931ff6270d), f64::from_bits(0x402d250670ed7a6c), f64::from_bits(0x404e517b9b494d38), f64::from_bits(0x405cb02b7433a838), f64::from_bits(0x405a03e606a1b871), f64::from_bits(0x4045c98d4308dbcd), f64::from_bits(0x401d115c4ce0540c), f64::from_bits(0x3fd4213e72b24b3a), ); let p_den = f_estrin_polyeval8( recip, f64::from_bits(0x3ff0000000000000), f64::from_bits(0x402681096aa3a87d), f64::from_bits(0x404623ab8d72ceea), f64::from_bits(0x40530af06ea802b2), f64::from_bits(0x404d526906fb9cec), f64::from_bits(0x403281caca389f1b), f64::from_bits(0x3ffdb93996948bb4), f64::from_bits(0x3f9a009da07eb989), ); let v = p_num / p_den; let pp = v * r_sqrt; pp as f32 } #[cfg(test)] mod tests { use super::*; #[test] fn test_k1f() { assert_eq!(f_k1ef(0.00000000005423), 18439980000.0); assert_eq!(f_k1ef(0.0000000043123), 231894820.0); assert_eq!(f_k1ef(0.3), 4.125158); assert_eq!(f_k1ef(1.89), 1.0710458); assert_eq!(f_k1ef(5.89), 0.5477655); assert_eq!(f_k1ef(101.89), 0.12461915); assert_eq!(f_k1ef(0.), f32::INFINITY); assert_eq!(f_k1ef(-0.), f32::INFINITY); assert!(f_k1ef(-0.5).is_nan()); assert!(f_k1ef(f32::NEG_INFINITY).is_nan()); assert_eq!(f_k1ef(f32::INFINITY), 0.); } }