pxfm/gamma/
lgamma_rf.rs

1/*
2 * // Copyright (c) Radzivon Bartoshyk 9/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, is_integerf, is_odd_integerf};
30use crate::floor::FloorFinite;
31use crate::logs::simple_fast_log;
32use crate::polyeval::{f_estrin_polyeval4, f_polyeval5, f_polyeval7, f_polyeval8};
33use crate::sin_cosf::fast_sinpif;
34
35#[inline]
36pub(crate) fn lgamma_coref(x: f32) -> (f64, i32) {
37    let ax = f32::from_bits(x.to_bits() & 0x7fff_ffff);
38    let dx = ax as f64;
39
40    let is_positive = x.is_sign_positive();
41    let mut sum_parity = 1f64;
42
43    let mut f_res = 0f64;
44
45    let mut signgam: i32 = 1i32;
46
47    // For negative x, since (G is gamma function)
48    // -x*G(-x)*G(x) = pi/sin(pi*x),
49    // we have
50    // G(x) = pi/(sin(pi*x)*(-x)*G(-x))
51    // since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0
52    // Hence, for x<0, signgam = sign(sin(pi*x)) and
53    // lgamma(x) = log(|Gamma(x)|)
54    //  = log(pi/(|x*sin(pi*x)|)) - lgamma(-x);
55    if !is_positive {
56        let y1 = ax.floor_finite();
57        let fraction = ax - y1; // excess over the boundary
58
59        let a = fast_sinpif(fraction);
60
61        sum_parity = -1.;
62        const LOG_PI: f64 = f64::from_bits(0x3ff250d048e7a1bd);
63        f_res = LOG_PI - simple_fast_log(a * dx);
64        // gamma(x) is negative in (-2n-1,-2n), thus when fx is odd
65        let is_odd = (!is_odd_integerf(y1)) as i32;
66        signgam = 1 - (is_odd << 1);
67    }
68
69    if ax <= 0.007 {
70        // Generated by Wolfram Mathematica:
71        // <<FunctionApproximations`
72        // ClearAll["Global`*"]
73        // f[x_]:=LogGamma[x+1]
74        // {err0,approx}=MiniMaxApproximation[f[x],{x,{2^-32,0.07},3,3},WorkingPrecision->90]
75        // num=Numerator[approx][[1]];
76        // den=Denominator[approx][[1]];
77        // poly=num;
78        // coeffs=CoefficientList[poly,x];
79        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
80        // poly=den;
81        // coeffs=CoefficientList[poly,x];
82        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
83        let num = f_estrin_polyeval4(
84            dx,
85            f64::from_bits(0xbb49bbc47e595350),
86            f64::from_bits(0xbfe2788cfc6fb2d3),
87            f64::from_bits(0x3fc829299b97c525),
88            f64::from_bits(0x3fd8e2e8aab1247c),
89        );
90        let den = f_estrin_polyeval4(
91            dx,
92            f64::from_bits(0x3ff0000000000000),
93            f64::from_bits(0x3ff190e5bf5a8162),
94            f64::from_bits(0x3fc927632366c502),
95            f64::from_bits(0xbf8b4ea7bd1b808e),
96        );
97        f_res = f_fmla(num / den - simple_fast_log(dx), sum_parity, f_res);
98    } else if ax < 1. {
99        // Poly for loggamma(x + 1) - log(x) generated by Wolfram Mathematica
100        // <<FunctionApproximations`
101        // ClearAll["Global`*"]
102        // f[x_]:=LogGamma[x+1]
103        // {err0, approx}=MiniMaxApproximation[f[z],{z,{0.06,0.9999999},6,6},WorkingPrecision->90]
104        // num=Numerator[approx][[1]];
105        // den=Denominator[approx][[1]];
106        // poly=den;
107        // coeffs=CoefficientList[poly,z];
108        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
109        let num = f_polyeval7(
110            dx,
111            f64::from_bits(0xbd24e4cf78c8818a),
112            f64::from_bits(0xbfe2788cfc6f64e6),
113            f64::from_bits(0xbfe1ea632fe853b2),
114            f64::from_bits(0x3fd988d2daad3806),
115            f64::from_bits(0x3fe1f4870eaafdf4),
116            f64::from_bits(0x3fc51e12d5b97330),
117            f64::from_bits(0x3f889ebeb9f0c8ff),
118        );
119        let den = f_polyeval7(
120            dx,
121            f64::from_bits(0x3ff0000000000000),
122            f64::from_bits(0x4003289872066f33),
123            f64::from_bits(0x4000373d88c32fe5),
124            f64::from_bits(0x3fe71e95bc874164),
125            f64::from_bits(0x3fb9936a0e008be7),
126            f64::from_bits(0x3f6cba20a1988a60),
127            f64::from_bits(0xbef3ca884ba4237a),
128        );
129        f_res = f_fmla(num / den - simple_fast_log(dx), sum_parity, f_res);
130    } else if ax <= 4. {
131        // Poly for loggamma(x + 1) - log(x) generated by Wolfram Mathematica
132        // <<FunctionApproximations`
133        // ClearAll["Global`*"]
134        // f[x_]:=LogGamma[x+1]
135        // {err0, approx}=MiniMaxApproximation[f[z],{z,{1.0000001,4},7,7},WorkingPrecision->90]
136        // num=Numerator[approx][[1]];
137        // den=Denominator[approx][[1]];
138        // poly=den;
139        // coeffs=CoefficientList[poly,z];
140        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
141        let num = f_polyeval8(
142            dx,
143            f64::from_bits(0xbe630847302a205f),
144            f64::from_bits(0xbfe2788c24a7deff),
145            f64::from_bits(0xbfdd0f61b8171907),
146            f64::from_bits(0x3fdab4ec27801e42),
147            f64::from_bits(0x3fde11fd49427a3c),
148            f64::from_bits(0x3fc0dbce39c660da),
149            f64::from_bits(0x3f88e0b4cd3e97f7),
150            f64::from_bits(0x3f328fcd9e9ee94e),
151        );
152        let den = f_polyeval8(
153            dx,
154            f64::from_bits(0x3ff0000000000000),
155            f64::from_bits(0x4001b135a31bffae),
156            f64::from_bits(0x3ffbbecb5e39f4ea),
157            f64::from_bits(0x3fe2e4da095d10bd),
158            f64::from_bits(0x3fb63cb7548d0d30),
159            f64::from_bits(0x3f73a67613163399),
160            f64::from_bits(0x3f10edd4e2b80e6f),
161            f64::from_bits(0xbe77f4e4068f9d32),
162        );
163        f_res = f_fmla(num / den - simple_fast_log(dx), sum_parity, f_res);
164    } else if ax <= 12. {
165        // Poly for loggamma(x) generated by Wolfram Mathematica
166        // <<FunctionApproximations`
167        // ClearAll["Global`*"]
168        // f[x_]:=LogGamma[x]
169        // {err0, approx}=MiniMaxApproximation[f[z],{z,{4,12},7,7},WorkingPrecision->90]
170        // num=Numerator[approx][[1]];
171        // den=Denominator[approx][[1]];
172        // poly=den;
173        // coeffs=CoefficientList[poly,z];
174        // TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
175        let num = f_polyeval8(
176            dx,
177            f64::from_bits(0x40086069fcc21690),
178            f64::from_bits(0x4008c8ef8602e78d),
179            f64::from_bits(0xc0186323149757ae),
180            f64::from_bits(0xbff6a9ded42d3f31),
181            f64::from_bits(0x3ff1adb5566cd807),
182            f64::from_bits(0x3fcff023390dead6),
183            f64::from_bits(0x3f8977a5454ec393),
184            f64::from_bits(0x3f21361088f694dc),
185        );
186        let den = f_polyeval8(
187            dx,
188            f64::from_bits(0x3ff0000000000000),
189            f64::from_bits(0x4016096b397dbe73),
190            f64::from_bits(0x401478926593a395),
191            f64::from_bits(0x3ff6a931f9e31511),
192            f64::from_bits(0x3fc0fbe57bf263a4),
193            f64::from_bits(0x3f7023f51ba39a98),
194            f64::from_bits(0x3efabd75ebfbde40),
195            f64::from_bits(0xbe4b2c9e2e4e7daa),
196        );
197        f_res = f_fmla(num / den, sum_parity, f_res);
198    } else {
199        // Stirling's approximation of Log(Gamma) and then Exp[Log[Gamma]]
200        let y_recip = 1. / dx;
201        let y_sqr = y_recip * y_recip;
202        // Bernoulli coefficients generated by SageMath:
203        // var('x')
204        // def bernoulli_terms(x, N):
205        //     S = 0
206        //     for k in range(1, N+1):
207        //         B = bernoulli(2*k)
208        //         term = (B / (2*k*(2*k-1))) * x**((2*k-1))
209        //         S += term
210        //     return S
211        //
212        // terms = bernoulli_terms(x, 5)
213        let bernoulli_poly = f_polyeval5(
214            y_sqr,
215            f64::from_bits(0x3fb5555555555555),
216            f64::from_bits(0xbf66c16c16c16c17),
217            f64::from_bits(0x3f4a01a01a01a01a),
218            f64::from_bits(0xbf43813813813814),
219            f64::from_bits(0x3f4b951e2b18ff23),
220        );
221        // Log[Gamma(x)] = x*log(x) - x + 1/2*Log(2*PI/x) + bernoulli_terms
222        const LOG2_PI_OVER_2: f64 = f64::from_bits(0x3fed67f1c864beb5);
223        let mut log_gamma = f_fmla(bernoulli_poly, y_recip, -dx) + LOG2_PI_OVER_2;
224        log_gamma = f_fmla(simple_fast_log(dx), dx - 0.5, log_gamma);
225        f_res = f_fmla(log_gamma, sum_parity, f_res);
226    }
227
228    (f_res, signgam)
229}
230
231/// Computes log(gamma(x))
232///
233/// Returns gamma value + sign.
234///
235/// ulp 0.5
236pub fn f_lgamma_rf(x: f32) -> (f32, i32) {
237    // filter out exceptional cases
238    let xb = x.to_bits().wrapping_shl(1);
239    if xb >= 0xffu32 << 24 || xb == 0 {
240        if x.is_infinite() {
241            return (f32::INFINITY, 1);
242        }
243        if x.is_nan() {
244            return (f32::NAN, 1);
245        }
246        if xb == 0 {
247            return (f32::INFINITY, 1 - 2 * (x.to_bits() >> 31) as i32);
248        }
249    }
250
251    if is_integerf(x) {
252        if x == 2. || x == 1. {
253            return (0., 1);
254        }
255        if x.is_sign_negative() {
256            let is_odd = (!is_odd_integerf(x)) as i32;
257            return (f32::INFINITY, 1 - (is_odd << 1));
258        }
259    }
260    let c_gamma = lgamma_coref(x);
261    let fx = c_gamma.0 as f32;
262    (fx, c_gamma.1)
263}
264
265#[cfg(test)]
266mod tests {
267    use crate::f_lgamma_rf;
268
269    #[test]
270    fn test_lgamma_rf() {
271        assert_eq!(f_lgamma_rf(f32::NEG_INFINITY), (f32::INFINITY, 1));
272        assert_eq!(f_lgamma_rf(f32::INFINITY), (f32::INFINITY, 1));
273        assert_eq!(f_lgamma_rf(0.), (f32::INFINITY, 1));
274        assert_eq!(f_lgamma_rf(-0.), (f32::INFINITY, -1));
275        assert_eq!(f_lgamma_rf(5.), (3.1780539, 1));
276        assert_eq!(f_lgamma_rf(-4.5), (-2.8130841, -1));
277        assert_eq!(f_lgamma_rf(-2.0015738), (5.7596655, -1));
278    }
279}