Skip to content

Commit 7503be4

Browse files
cohomologydedekindring
authored andcommitted
Fix lower incomplete gamma functions with x = 0
In this case, the errno error handling did not work correctly, as internal functions where accidently setting it, although no overflow happens. Fixes #1249.
1 parent a5c0625 commit 7503be4

File tree

3 files changed

+155
-5
lines changed

3 files changed

+155
-5
lines changed

include/boost/math/special_functions/gamma.hpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,7 @@ BOOST_MATH_GPU_ENABLED T full_igamma_prefix(T a, T z, const Policy& pol)
908908
{
909909
BOOST_MATH_STD_USING
910910

911-
if (z > tools::max_value<T>())
911+
if (z > tools::max_value<T>() || (a > 0 && z == 0))
912912
return 0;
913913

914914
T alz = a * log(z);
@@ -962,7 +962,7 @@ template <class T, class Policy, class Lanczos>
962962
BOOST_MATH_GPU_ENABLED T regularised_gamma_prefix(T a, T z, const Policy& pol, const Lanczos& l)
963963
{
964964
BOOST_MATH_STD_USING
965-
if (z >= tools::max_value<T>())
965+
if (z >= tools::max_value<T>() || (a > 0 && z == 0))
966966
return 0;
967967
T agh = a + static_cast<T>(Lanczos::g()) - T(0.5);
968968
T prefix;
@@ -1297,7 +1297,11 @@ BOOST_MATH_GPU_ENABLED T gamma_incomplete_imp_final(T a, T x, bool normalised, b
12971297

12981298
int eval_method;
12991299

1300-
if(is_int && (x > 0.6))
1300+
if (x == 0)
1301+
{
1302+
eval_method = 2;
1303+
}
1304+
else if(is_int && (x > 0.6))
13011305
{
13021306
// calculate Q via finite sum:
13031307
invert = !invert;
@@ -1627,7 +1631,7 @@ BOOST_MATH_GPU_ENABLED T gamma_incomplete_imp_final(T a, T x, bool normalised, b
16271631
#endif
16281632
result = gam - result;
16291633
}
1630-
if(p_derivative)
1634+
if(p_derivative && x > 0)
16311635
{
16321636
//
16331637
// Need to convert prefix term to derivative:
@@ -1660,7 +1664,7 @@ BOOST_MATH_GPU_ENABLED T gamma_incomplete_imp(T a, T x, bool normalised, bool in
16601664

16611665
T result = 0; // Just to avoid warning C4701: potentially uninitialized local variable 'result' used
16621666

1663-
if(a >= max_factorial<T>::value && !normalised)
1667+
if(x > 0 && a >= max_factorial<T>::value && !normalised)
16641668
{
16651669
//
16661670
// When we're computing the non-normalized incomplete gamma

test/Jamfile.v2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ test-suite special_fun :
194194
[ run git_issue_1139.cpp ]
195195
[ run git_issue_1175.cpp ]
196196
[ run git_issue_1194.cpp ]
197+
[ run git_issue_1249.cpp /boost/test//boost_unit_test_framework ]
197198
[ run special_functions_test.cpp /boost/test//boost_unit_test_framework ]
198199
[ run test_airy.cpp test_instances//test_instances pch_light /boost/test//boost_unit_test_framework ]
199200
[ run test_bessel_j.cpp test_instances//test_instances pch_light /boost/test//boost_unit_test_framework ]

test/git_issue_1249.cpp

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// (C) Copyright Kilian Kilger 2025.
2+
// Use, modification and distribution are subject to the
3+
// Boost Software License, Version 1.0. (See accompanying file
4+
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5+
6+
#define BOOST_TEST_MAIN
7+
8+
#include <boost/test/unit_test.hpp>
9+
#include <boost/test/tools/floating_point_comparison.hpp>
10+
#include <boost/test/unit_test.hpp>
11+
#include <boost/test/results_collector.hpp>
12+
#include <boost/math/special_functions/gamma.hpp>
13+
#include <boost/multiprecision/cpp_bin_float.hpp>
14+
15+
using namespace std;
16+
using namespace boost::math;
17+
using namespace boost::math::policies;
18+
using namespace boost::multiprecision;
19+
20+
typedef policy<
21+
policies::domain_error<errno_on_error>,
22+
policies::pole_error<errno_on_error>,
23+
policies::overflow_error<errno_on_error>,
24+
policies::evaluation_error<errno_on_error>
25+
> c_policy;
26+
27+
template<typename T>
28+
struct test_lower
29+
{
30+
T operator()(T a, T x) const
31+
{
32+
return tgamma_lower(a, x, c_policy());
33+
}
34+
35+
T expected(T a) const
36+
{
37+
return T(0.0);
38+
}
39+
};
40+
41+
template<typename T>
42+
struct test_upper
43+
{
44+
T operator()(T a, T x) const
45+
{
46+
return tgamma(a, x, c_policy());
47+
}
48+
T expected(T a) const
49+
{
50+
return tgamma(a, c_policy());
51+
}
52+
};
53+
54+
template<typename T>
55+
struct test_gamma_p
56+
{
57+
T operator()(T a, T x) const
58+
{
59+
return gamma_p(a, x, c_policy());
60+
}
61+
T expected(T) const
62+
{
63+
return T(0.0);
64+
}
65+
};
66+
67+
template<typename T>
68+
struct test_gamma_q
69+
{
70+
T operator()(T a, T x) const
71+
{
72+
return gamma_q(a, x, c_policy());
73+
}
74+
T expected(T) const
75+
{
76+
return T(1.0);
77+
}
78+
};
79+
80+
template<typename T, template<typename> class Fun>
81+
void test_impl(T a)
82+
{
83+
Fun<T> fn;
84+
errno = 0;
85+
T x = T(0.0);
86+
T result = fn(a, x);
87+
int saveErrno = errno;
88+
89+
errno = 0;
90+
91+
T expected = fn.expected(a);
92+
93+
BOOST_CHECK(errno == saveErrno);
94+
BOOST_CHECK_EQUAL(result, expected);
95+
}
96+
97+
template<template<typename> class Fun, typename T>
98+
void test_type_dispatch(T val)
99+
{
100+
if (val <= (std::numeric_limits<float>::max)())
101+
test_impl<float, Fun>(static_cast<float>(val));
102+
if (val <= (std::numeric_limits<double>::max)())
103+
test_impl<double, Fun>(static_cast<double>(val));
104+
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
105+
test_impl<long double, Fun>(static_cast<long double>(val));
106+
#endif
107+
test_impl<cpp_bin_float_50, Fun>(static_cast<cpp_bin_float_50>(val));
108+
}
109+
110+
template<template<typename> class Fun>
111+
void test_impl()
112+
{
113+
test_type_dispatch<Fun, double>(1.0);
114+
test_type_dispatch<Fun, double>(0.1);
115+
test_type_dispatch<Fun, double>(0.5);
116+
test_type_dispatch<Fun, double>(0.6);
117+
test_type_dispatch<Fun, double>(1.3);
118+
test_type_dispatch<Fun, double>(1.5);
119+
test_type_dispatch<Fun, double>(2);
120+
test_type_dispatch<Fun, double>(100);
121+
test_type_dispatch<Fun, double>((std::numeric_limits<float>::max)());
122+
test_type_dispatch<Fun, double>((std::numeric_limits<double>::max)());
123+
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
124+
test_type_dispatch<Fun, long double>((std::numeric_limits<long double>::max)());
125+
#endif
126+
}
127+
128+
void test_derivative()
129+
{
130+
double derivative = 0;
131+
double result = boost::math::detail::gamma_incomplete_imp(1.0, 0.0,
132+
true, false, c_policy(), &derivative);
133+
BOOST_CHECK(errno == 0);
134+
BOOST_CHECK_EQUAL(derivative, 0);
135+
BOOST_CHECK_EQUAL(result, 0);
136+
}
137+
138+
BOOST_AUTO_TEST_CASE( test_main )
139+
{
140+
test_impl<test_lower>();
141+
test_impl<test_upper>();
142+
test_impl<test_gamma_p>();
143+
test_impl<test_gamma_q>();
144+
test_derivative();
145+
}

0 commit comments

Comments
 (0)