@@ -173,7 +173,7 @@ pub fn tgamma(x: f64) -> Result<f64, Error> {
173
173
q - absx
174
174
} ;
175
175
let z = z * LANCZOS_G / y;
176
- let r = if x < 0.0 {
176
+ let mut r = if x < 0.0 {
177
177
let mut r = -PI / m_sinpi ( absx) / absx * y. exp ( ) / lanczos_sum ( absx) ;
178
178
r -= z * r;
179
179
if absx < 140.0 {
@@ -196,6 +196,7 @@ pub fn tgamma(x: f64) -> Result<f64, Error> {
196
196
}
197
197
r
198
198
} ;
199
+
199
200
if r. is_infinite ( ) {
200
201
return Err ( ( f64:: INFINITY , Error :: ERANGE ) . 1 ) ;
201
202
} else {
@@ -241,7 +242,14 @@ pub fn lgamma(x: f64) -> Result<f64, Error> {
241
242
242
243
if x < 0.0 {
243
244
// Use reflection formula to get value for negative x
244
- r = LOG_PI - m_sinpi ( absx) . abs ( ) . ln ( ) - absx. ln ( ) - r;
245
+
246
+ // Calculate the sin(pi * x) value using m_sinpi
247
+ let sinpi_val = m_sinpi ( absx) ;
248
+
249
+ // In CPython, the expression is:
250
+ // r = logpi - log(fabs(m_sinpi(absx))) - log(absx) - r;
251
+ // We'll match this order exactly
252
+ r = LOG_PI - sinpi_val. abs ( ) . ln ( ) - absx. ln ( ) - r;
245
253
}
246
254
if r. is_infinite ( ) {
247
255
return Err ( Error :: ERANGE ) ;
@@ -283,6 +291,60 @@ mod tests {
283
291
}
284
292
}
285
293
294
+ #[ test]
295
+ fn test_specific_lgamma_value ( ) {
296
+ let x = -3.8510064710745118 ;
297
+ let rs_lgamma = lgamma ( x) . unwrap ( ) ;
298
+
299
+ pyo3:: prepare_freethreaded_python ( ) ;
300
+ Python :: with_gil ( |py| {
301
+ let math = PyModule :: import ( py, "math" ) . unwrap ( ) ;
302
+ let py_lgamma = math
303
+ . getattr ( "lgamma" )
304
+ . unwrap ( )
305
+ . call1 ( ( x, ) )
306
+ . unwrap ( )
307
+ . extract :: < f64 > ( )
308
+ . unwrap ( ) ;
309
+
310
+ println ! ( "x = {}" , x) ;
311
+ println ! ( "Python lgamma = {} ({:x})" , py_lgamma, unsafe {
312
+ std:: mem:: transmute:: <f64 , u64 >( py_lgamma)
313
+ } ) ;
314
+ println ! ( "Rust lgamma = {} ({:x})" , rs_lgamma, unsafe {
315
+ std:: mem:: transmute:: <f64 , u64 >( rs_lgamma)
316
+ } ) ;
317
+
318
+ // Print intermediate values
319
+ let absx = x. abs ( ) ;
320
+ let sinpi_val = m_sinpi ( absx) ;
321
+
322
+ println ! ( "absx = {}" , absx) ;
323
+ println ! ( "m_sinpi = {}" , sinpi_val) ;
324
+
325
+ // Compare with Python's sin(pi * x)
326
+ let py_code = PyModule :: from_code (
327
+ py,
328
+ c"import math\n def sinpi(x): return math.sin(math.pi * x)\n " ,
329
+ c"" ,
330
+ c"" ,
331
+ )
332
+ . unwrap ( ) ;
333
+ let py_sinpi = py_code
334
+ . getattr ( "sinpi" )
335
+ . unwrap ( )
336
+ . call1 ( ( absx, ) )
337
+ . unwrap ( )
338
+ . extract :: < f64 > ( )
339
+ . unwrap ( ) ;
340
+ println ! ( "Python sinpi = {}" , py_sinpi) ;
341
+
342
+ let py_lgamma_repr = unsafe { std:: mem:: transmute :: < f64 , u64 > ( py_lgamma) } ;
343
+ let rs_lgamma_repr = unsafe { std:: mem:: transmute :: < f64 , u64 > ( rs_lgamma) } ;
344
+ println ! ( "Bit difference: {}" , py_lgamma_repr ^ rs_lgamma_repr) ;
345
+ } ) ;
346
+ }
347
+
286
348
proptest ! {
287
349
#[ test]
288
350
fn test_tgamma( x: f64 ) {
@@ -300,9 +362,9 @@ mod tests {
300
362
} ;
301
363
let py_gamma_repr = unsafe { std:: mem:: transmute:: <f64 , i64 >( py_gamma) } ;
302
364
let rs_gamma_repr = unsafe { std:: mem:: transmute:: <f64 , i64 >( rs_gamma) } ;
303
- // assert_eq!(py_gamma_repr, rs_gamma_repr, "x = {x}, py_gamma = {py_gamma}, rs_gamma = {rs_gamma}");
365
+ assert_eq!( py_gamma_repr, rs_gamma_repr, "x = {x}, py_gamma = {py_gamma}, rs_gamma = {rs_gamma}" ) ;
304
366
// allow 1 bit error for now
305
- assert!( ( py_gamma_repr - rs_gamma_repr) . abs( ) <= 1 , "x = {x} diff: {}, py_gamma = {py_gamma} ({py_gamma_repr:x}), rs_gamma = {rs_gamma} ({rs_gamma_repr:x})" , py_gamma_repr ^ rs_gamma_repr) ;
367
+ // assert!((py_gamma_repr - rs_gamma_repr).abs() <= 1, "x = {x} diff: {}, py_gamma = {py_gamma} ({py_gamma_repr:x}), rs_gamma = {rs_gamma} ({rs_gamma_repr:x})", py_gamma_repr ^ rs_gamma_repr);
306
368
} ) ;
307
369
}
308
370
0 commit comments