diff --git a/include/boost/math/special_functions/gamma.hpp b/include/boost/math/special_functions/gamma.hpp index c2f390713..f8a3e51bd 100644 --- a/include/boost/math/special_functions/gamma.hpp +++ b/include/boost/math/special_functions/gamma.hpp @@ -164,7 +164,9 @@ T gamma_imp(T z, const Policy& pol, const Lanczos& l) } else if (z < tools::root_epsilon()) { - return 1 / z - constants::euler(); + if (z < 1 / tools::max_value()) + result = policies::raise_overflow_error(function, 0, pol); + result *= 1 / z - constants::euler(); } else { @@ -242,7 +244,10 @@ T lgamma_imp(T z, const Policy& pol, const Lanczos& l, int* sign = 0) { if (0 == z) return policies::raise_pole_error(function, "Evaluation of lgamma at %1%.", z, pol); - result = log(fabs(1 / z - constants::euler())); + if (fabs(z) < 1 / tools::max_value()) + result = -log(z); + else + result = log(fabs(1 / z - constants::euler())); if (z < 0) sresult = -1; } diff --git a/test/test_gamma.cpp b/test/test_gamma.cpp index b225d1f45..fafed3125 100644 --- a/test/test_gamma.cpp +++ b/test/test_gamma.cpp @@ -308,12 +308,12 @@ BOOST_AUTO_TEST_CASE( test_main ) BOOST_MATH_CONTROL_FP; #ifndef BOOST_MATH_BUGGY_LARGE_FLOAT_CONSTANTS - test_spots(0.0F); + test_spots(0.0F, "float"); #endif - test_spots(0.0); + test_spots(0.0, "double"); #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS - test_spots(0.0L); - test_spots(boost::math::concepts::real_concept(0.1)); + test_spots(0.0L, "long double"); + test_spots(boost::math::concepts::real_concept(0.1), "real_concept"); #endif #ifndef BOOST_MATH_BUGGY_LARGE_FLOAT_CONSTANTS diff --git a/test/test_gamma.hpp b/test/test_gamma.hpp index d17ff76ce..ce9ce58a5 100644 --- a/test/test_gamma.hpp +++ b/test/test_gamma.hpp @@ -165,13 +165,20 @@ void test_gamma(T, const char* name) } template -void test_spots(T) +void test_spots(T, const char* name) { BOOST_MATH_STD_USING + + std::cout << "Testing type " << name << std::endl; // // basic sanity checks, tolerance is 50 epsilon expressed as a percentage: // T tolerance = boost::math::tools::epsilon() * 5000; + // + // Extra tolerance for real_concept checks which use less accurate code: + // + T extra_tol = boost::is_floating_point::value ? 1 : 20; + BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(3.5)), static_cast(3.3233509704478425511840640312646472177454052302295L), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(0.125)), static_cast(7.5339415987976119046992298412151336246104195881491L), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(static_cast(-0.125)), static_cast(-8.7172188593831756100190140408231437691829605421405L), tolerance); @@ -202,6 +209,24 @@ void test_spots(T) BOOST_CHECK_CLOSE(::boost::math::tgamma(-ldexp(static_cast(1), -66)), static_cast(-7.37869762948382064645772156649015328606199162983179574406439e19L), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(-ldexp(static_cast(1), -33)), static_cast(-8.58993459257721566501667413261977598620193488449233402857632e9L), tolerance); BOOST_CHECK_CLOSE(::boost::math::tgamma(-4 / boost::math::tools::max_value()), -boost::math::tools::max_value() / 4, tolerance); + BOOST_CHECK_CLOSE(::boost::math::tgamma(-1 + ldexp(static_cast(1), -22)), static_cast(-4.19430442278467170746130758391572421252211886167956799318843e6L), tolerance); + BOOST_CHECK_CLOSE(::boost::math::tgamma(-1 - ldexp(static_cast(1), -22)), static_cast(4.19430357721600151046968956086404748206205391186399889108944e6L), tolerance); + BOOST_CHECK_CLOSE(::boost::math::tgamma(-4 + ldexp(static_cast(1), -20)), static_cast(43690.7294216755534842491085530510391932288379640970386378756L), tolerance * extra_tol); + BOOST_CHECK_CLOSE(::boost::math::tgamma(-4 - ldexp(static_cast(1), -20)), static_cast(-43690.6039118698506165317137699180871126338425941292693705533L), tolerance * extra_tol); + if (boost::math::tools::digits() > 50) + { + BOOST_CHECK_CLOSE(::boost::math::tgamma(-1 + ldexp(static_cast(1), -44)), static_cast(-1.75921860444164227843350985473932247549232492467032584051825e13L), tolerance); + BOOST_CHECK_CLOSE(::boost::math::tgamma(-1 - ldexp(static_cast(1), -44)), static_cast(1.75921860444155772156649016131144377791001546933519242218430e13L), tolerance); + BOOST_CHECK_CLOSE(::boost::math::tgamma(-4 + ldexp(static_cast(1), -44)), static_cast(7.33007751850729421569517998006564998020333048893618664936994e11L), tolerance * extra_tol); + BOOST_CHECK_CLOSE(::boost::math::tgamma(-4 - ldexp(static_cast(1), -44)), static_cast(-7.33007751850603911763815347967171096249288790373790093559568e11L), tolerance * extra_tol); + } + if (boost::math::tools::digits() > 60) + { + BOOST_CHECK_CLOSE(::boost::math::tgamma(-1 + ldexp(static_cast(1), -55)), static_cast(-3.60287970189639684227843350984671785799289582631555600561524e16L), tolerance); + BOOST_CHECK_CLOSE(::boost::math::tgamma(-1 - ldexp(static_cast(1), -55)), static_cast(3.60287970189639675772156649015328997929531384279596450489170e16L), tolerance); + BOOST_CHECK_CLOSE(::boost::math::tgamma(-4 + ldexp(static_cast(1), -55)), static_cast(1.50119987579016539608823618465835611632004877549994080474627e15L), tolerance * extra_tol); + BOOST_CHECK_CLOSE(::boost::math::tgamma(-4 - ldexp(static_cast(1), -55)), static_cast(-1.50119987579016527057843048200831672241827850458884790004313e15L), tolerance * extra_tol); + } #ifdef BOOST_MSVC #pragma warning(push) @@ -274,5 +299,35 @@ void test_spots(T) BOOST_CHECK(sign == -1); BOOST_CHECK_CLOSE(::boost::math::lgamma(-4 / boost::math::tools::max_value(), &sign), log(boost::math::tools::max_value() / 4), tolerance); BOOST_CHECK(sign == -1); + BOOST_CHECK_CLOSE(::boost::math::lgamma(-1 + ldexp(static_cast(1), -22), &sign), log(static_cast(4.19430442278467170746130758391572421252211886167956799318843e6L)), tolerance); + BOOST_CHECK(sign == -1); + BOOST_CHECK_CLOSE(::boost::math::lgamma(-1 - ldexp(static_cast(1), -22), &sign), log(static_cast(4.19430357721600151046968956086404748206205391186399889108944e6L)), tolerance); + BOOST_CHECK(sign == 1); + BOOST_CHECK_CLOSE(::boost::math::lgamma(-4 + ldexp(static_cast(1), -20), &sign), log(static_cast(43690.7294216755534842491085530510391932288379640970386378756L)), tolerance * extra_tol); + BOOST_CHECK(sign == 1); + BOOST_CHECK_CLOSE(::boost::math::lgamma(-4 - ldexp(static_cast(1), -20), &sign), log(static_cast(43690.6039118698506165317137699180871126338425941292693705533L)), tolerance * extra_tol); + BOOST_CHECK(sign == -1); + if (boost::math::tools::digits() > 50) + { + BOOST_CHECK_CLOSE(::boost::math::lgamma(-1 + ldexp(static_cast(1), -44), &sign), log(static_cast(1.75921860444164227843350985473932247549232492467032584051825e13L)), tolerance); + BOOST_CHECK(sign == -1); + BOOST_CHECK_CLOSE(::boost::math::lgamma(-1 - ldexp(static_cast(1), -44), &sign), log(static_cast(1.75921860444155772156649016131144377791001546933519242218430e13L)), tolerance); + BOOST_CHECK(sign == 1); + BOOST_CHECK_CLOSE(::boost::math::lgamma(-4 + ldexp(static_cast(1), -44), &sign), log(static_cast(7.33007751850729421569517998006564998020333048893618664936994e11L)), tolerance * extra_tol); + BOOST_CHECK(sign == 1); + BOOST_CHECK_CLOSE(::boost::math::lgamma(-4 - ldexp(static_cast(1), -44), &sign), log(static_cast(7.33007751850603911763815347967171096249288790373790093559568e11L)), tolerance * extra_tol); + BOOST_CHECK(sign == -1); + } + if (boost::math::tools::digits() > 60) + { + BOOST_CHECK_CLOSE(::boost::math::lgamma(-1 + ldexp(static_cast(1), -55), &sign), log(static_cast(3.60287970189639684227843350984671785799289582631555600561524e16L)), tolerance); + BOOST_CHECK(sign == -1); + BOOST_CHECK_CLOSE(::boost::math::lgamma(-1 - ldexp(static_cast(1), -55), &sign), log(static_cast(3.60287970189639675772156649015328997929531384279596450489170e16L)), tolerance); + BOOST_CHECK(sign == 1); + BOOST_CHECK_CLOSE(::boost::math::lgamma(-4 + ldexp(static_cast(1), -55), &sign), log(static_cast(1.50119987579016539608823618465835611632004877549994080474627e15L)), tolerance * extra_tol); + BOOST_CHECK(sign == 1); + BOOST_CHECK_CLOSE(::boost::math::lgamma(-4 - ldexp(static_cast(1), -55), &sign), log(static_cast(1.50119987579016527057843048200831672241827850458884790004313e15L)), tolerance * extra_tol); + BOOST_CHECK(sign == -1); + } }