// Copyright John Maddock 2006. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Module build check for the series-summation tools in // . The component is consumed through // import boost.math and the public sum_series, checked_sum_series and // kahan_sum_series entry points are exercised. There is no runtime non-module // test for this component (only a compile-only test), so the expected values // are mathematically-exact closed forms: the geometric series sum over n >= 0 // of r^n equals 1 / (1 - r) for |r| < 1, and the exponential series sum over // n >= 0 of 1/n! equals e. #ifndef BOOST_MATH_BUILD_MODULE #include #include #else import boost.math; #endif #include "math_unit_test.hpp" // Yields the geometric sequence r^n for n = 0, 1, 2, ...; the partial sums // converge to 1 / (1 - r) when |r| < 1. template struct geometric_term { using result_type = Real; result_type ratio; result_type current; geometric_term(result_type r, result_type first = static_cast(1)) : ratio{r}, current{first} {} result_type operator()() { result_type value {current}; current *= ratio; return value; } }; // Yields the terms 1/n! for n = 0, 1, 2, ...; the partial sums converge to e. template struct exp_term { using result_type = Real; result_type term; int n; exp_term() : term{static_cast(1)}, n{0} {} result_type operator()() { result_type value {term}; ++n; term /= static_cast(n); return value; } }; template void test_spots(const char* type_name) { std::cout << "Testing series summation for type " << type_name << std::endl; const int bits {boost::math::numeric_limits::digits}; // Geometric series with r = 1/2: sum over n >= 0 of (1/2)^n is 2. { geometric_term func {static_cast(0.5)}; Real Q {boost::math::tools::sum_series(func, bits)}; CHECK_ULP_CLOSE(static_cast(2), Q, 4); } // Geometric series with r = 1/4: sum over n >= 0 of (1/4)^n is 4/3. { geometric_term func {static_cast(0.25)}; Real Q {boost::math::tools::sum_series(func, bits)}; CHECK_ULP_CLOSE(static_cast(4) / 3, Q, 4); } // Alternating geometric series with r = -1/2: sum over n >= 0 of (-1/2)^n // is 2/3. { geometric_term func {static_cast(-0.5)}; Real Q {boost::math::tools::sum_series(func, bits)}; CHECK_ULP_CLOSE(static_cast(2) / 3, Q, 8); } // max_terms is both the cap on the number of iterations and, on return, the // count of terms actually evaluated. A cap of 3 stops this slowly converging // series early, so exactly 3 terms are summed and max_terms comes back as 3. { geometric_term func {static_cast(0.5)}; const Real factor {std::ldexp(static_cast(1), 1 - bits)}; boost::math::uintmax_t max_terms {3}; boost::math::tools::sum_series(func, factor, max_terms); CHECK_EQUAL(static_cast(3), max_terms); } // checked_sum_series returns the same value as sum_series and additionally // accumulates the L1 norm sum of |term|. For the alternating series r = -1/2 // the value is 2/3 while the norm is the non-alternating geometric sum, 2. { geometric_term func {static_cast(-0.5)}; const Real factor {std::ldexp(static_cast(1), 1 - bits)}; boost::math::uintmax_t max_terms {1000}; Real norm {0}; Real Q {boost::math::tools::checked_sum_series(func, factor, max_terms, static_cast(0), norm)}; CHECK_ULP_CLOSE(static_cast(2) / 3, Q, 8); CHECK_ULP_CLOSE(static_cast(2), norm, 8); } // Kahan-compensated summation of the exponential series sum over n >= 0 of // 1/n!, which is e. { exp_term func {}; Real Q {boost::math::tools::kahan_sum_series(func, bits)}; CHECK_ULP_CLOSE(boost::math::constants::e(), Q, 16); } } int main() { test_spots("double"); test_spots("float"); return boost::math::test::report_errors(); }