1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
|
///////////////////////////////////////////////////////////////////
// Copyright Christopher Kormanyos 2020 - 2022. //
// Distributed under 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) //
///////////////////////////////////////////////////////////////////
#include <array>
#include <cstdint>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>
#define EXAMPLE008_BERNOULLI_USE_LOCAL_PI
#if !defined(BOOST_MP_STANDALONE)
#define BOOST_MP_STANDALONE
#endif
#if !defined(EXAMPLE008_BERNOULLI_USE_LOCAL_PI)
#if !defined(BOOST_MATH_STANDALONE)
#define BOOST_MATH_STANDALONE
#endif
#include <boost/math/constants/constants.hpp>
#endif
#include <boost/multiprecision/cpp_bin_float.hpp>
// cd /mnt/c/Users/User/Documents/Ks/PC_Software/Test
// g++ -Wall -Wextra -Wconversion -Wsign-conversion -Wshadow -Wundef -O3 -std=c++11 -I/mnt/c/boost/modular_boost/boost/libs/multiprecision/include -I/mnt/c/boost/modular_boost/boost/libs/config/include standalone_bernoulli_tgamma.cpp -o standalone_bernoulli_tgamma.exe
// D:\MinGW_nuwen\MinGW\bin\g++ -Wall -Wextra -Wconversion -Wsign-conversion -Wshadow -Wundef -O3 -std=c++11 -IC:\boost\modular_boost\boost\libs\multiprecision\include -IC:\boost\modular_boost\boost\libs\config\include test.cpp -o test.exe
namespace example008_bernoulli
{
constexpr std::int32_t wide_decimal_digits10 = INT32_C(1001);
using wide_float_backend_type = boost::multiprecision::cpp_bin_float<wide_decimal_digits10, boost::multiprecision::digit_base_10, std::allocator<void>>;
using wide_float_type = boost::multiprecision::number<wide_float_backend_type, boost::multiprecision::et_off>;
template<typename FloatingPointType>
auto pi() -> FloatingPointType
{
return static_cast<FloatingPointType>(3.1415926535897932384626433832795029L); // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
}
#if defined(EXAMPLE008_BERNOULLI_USE_LOCAL_PI)
template<typename FloatingPointType>
auto calc_pi() -> FloatingPointType
{
// Use a quadratically convergent Gauss AGM to compute pi.
using floating_point_type = FloatingPointType;
floating_point_type val_pi;
floating_point_type a(1U);
// Initialize bB to 0.5.
floating_point_type bB(0.5F); // NOLINT(readability-identifier-naming,cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
// Initialize t to 0.375.
floating_point_type t(static_cast<floating_point_type>(3U) / 8U); // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
floating_point_type s(bB);
// This loop is designed for a maximum of several million
// decimal digits of pi. The index k should reach no higher
// than about 25 or 30. After about 20 iterations, the precision
// is about one million decimal digits.
const auto digits10_iteration_goal =
static_cast<std::uint32_t>
(
static_cast<std::uint32_t>(std::numeric_limits<floating_point_type>::digits10 / 2)
+ static_cast<std::uint32_t>(9U)
);
using std::log;
using std::lround;
const auto digits10_scale =
static_cast<std::uint32_t>
(
lround
(
static_cast<float>(1000.0F * log(static_cast<float>(std::numeric_limits<floating_point_type>::radix))) / log(10.0F)
)
);
for(auto k = static_cast<unsigned>(UINT8_C(0));
k < static_cast<unsigned>(UINT8_C(48));
++k)
{
using std::sqrt;
a += sqrt(bB);
a /= 2U;
val_pi = a;
val_pi *= a;
bB = val_pi;
bB -= t;
bB *= 2U;
floating_point_type iterate_term(bB);
iterate_term -= val_pi;
iterate_term *= static_cast<unsigned long long>(1ULL << (k + 1U)); // NOLINT(google-runtime-int)
s += iterate_term;
// Test the number of precise digits from this iteration.
// If it is there are enough precise digits, then the calculation
// is finished.
const auto ib =
(std::max)
(
static_cast<std::int32_t>(0),
static_cast<std::int32_t>(-ilogb(iterate_term))
);
const auto digits10_of_iteration =
static_cast<std::uint32_t>
(
static_cast<std::uint64_t>(static_cast<std::uint64_t>(ib) * digits10_scale) / UINT32_C(1000)
);
// Estimate the approximate decimal digits of this iteration term.
// If we have attained about half of the total desired digits
// with this iteration term, then the calculation is finished
// because the change from the next iteration will be
// insignificantly small.
if(digits10_of_iteration > digits10_iteration_goal)
{
break;
}
t = val_pi;
t += bB;
t /= 4U;
}
return (val_pi + bB) / s;
}
template<typename FloatingPointType>
auto my_pi() -> const FloatingPointType&
{
using floating_point_type = FloatingPointType;
static const floating_point_type local_pi = calc_pi<floating_point_type>();
return local_pi;
}
#endif
template<>
auto pi() -> wide_float_type
{
#if defined(EXAMPLE008_BERNOULLI_USE_LOCAL_PI)
return my_pi<wide_float_type>();
#else
return boost::math::constants::pi<wide_float_type>();
#endif
}
auto bernoulli_table() -> std::vector<wide_float_type>&
{
static std::vector<wide_float_type>
bernoulli_table
(
static_cast<std::size_t>
(
static_cast<float>(std::numeric_limits<wide_float_type>::digits10) * 0.95F
)
);
return bernoulli_table;
}
template<typename FloatingPointType>
auto bernoulli_b(FloatingPointType* bn, std::uint32_t n) -> void
{
using floating_point_type = FloatingPointType;
// See reference "Computing Bernoulli and Tangent Numbers", Richard P. Brent.
// See also the book Richard P. Brent and Paul Zimmermann, "Modern Computer Arithmetic",
// Cambridge University Press, 2010, p. 237.
const auto m = static_cast<std::uint32_t>(n / 2U);
std::vector<floating_point_type> tangent_numbers(m + 1U);
tangent_numbers[0U] = 0U;
tangent_numbers[1U] = 1U;
for(std::uint32_t k = 1U; k < m; ++k)
{
tangent_numbers[k + 1U] = tangent_numbers[k] * k;
}
for(auto k = static_cast<std::uint32_t>(2U); k <= m; ++k)
{
for(auto j = k; j <= m; ++j)
{
const std::uint32_t j_minus_k = j - k;
tangent_numbers[j] = (tangent_numbers[j - 1] * j_minus_k)
+ (tangent_numbers[j] * (j_minus_k + 2U));
}
}
floating_point_type two_pow_two_m(4U);
for(std::uint32_t i = 1U; i < static_cast<std::uint32_t>(n / 2U); ++i)
{
const auto two_i = static_cast<std::uint32_t>(2U * i);
const floating_point_type b = (tangent_numbers[i] * two_i) / (two_pow_two_m * (two_pow_two_m - 1));
const bool b_neg = ((two_i % 4U) == 0U);
bn[two_i] = ((!b_neg) ? b : -b); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
two_pow_two_m *= 4U;
}
bn[0U] = 1U; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
bn[1U] = floating_point_type(-1) / 2U; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
template<typename FloatingPointType>
auto tgamma(const FloatingPointType& x) -> FloatingPointType
{
using floating_point_type = FloatingPointType;
// Check if the argument should be scaled up for the Bernoulli series expansion.
static const auto min_arg_n =
static_cast<std::int32_t>
(
static_cast<float>(static_cast<float>(std::numeric_limits<floating_point_type>::digits10) * 0.8F)
);
static const floating_point_type min_arg_x = floating_point_type(min_arg_n);
const auto n_recur =
static_cast<std::uint32_t>
(
(x < min_arg_x) ? static_cast<std::uint32_t>(static_cast<std::uint32_t>(min_arg_n - static_cast<std::int32_t>(x)) + 1U)
: static_cast<std::uint32_t>(0U)
);
floating_point_type xx(x);
// Scale the argument up and use downward recursion later for the final result.
if(n_recur != 0U)
{
xx += n_recur;
}
floating_point_type one_over_x_pow_two_n_minus_one = 1 / xx;
const floating_point_type one_over_x2 = one_over_x_pow_two_n_minus_one * one_over_x_pow_two_n_minus_one;
floating_point_type sum = (one_over_x_pow_two_n_minus_one * bernoulli_table()[2U]) / 2U;
floating_point_type tol = std::numeric_limits<floating_point_type>::epsilon();
using std::log;
if(xx > 8U) // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
{
// In the following code sequence, we extract the approximate logarithm
// of the argument x and use the leading term of Stirling's approximation,
// which is Log[Gamma[x]] aprox. (x (Log[x] - 1)) in order to scale
// the tolerance. In order to do this, we find the built-in floating point
// approximation of (x (Log[x] - 1)).
// Limit fx to the range 8 <= fx <= 10^16, where 8 is chosen to
// ensure that (log(fx) - 1.0F) remains positive and 10^16 is
// selected arbitrarily, yet ensured to be rather large.
auto fx_max = (std::max)(static_cast<floating_point_type>(8U), xx); // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
auto fx = (std::min)(fx_max, static_cast<floating_point_type>(UINT64_C(10000000000000000)));
tol *= static_cast<float>(fx * (log(fx) - 1.0F));
}
// Perform the Bernoulli series expansion.
for(auto n2 = static_cast<std::uint32_t>(4U); n2 < static_cast<std::uint32_t>(bernoulli_table().size()); n2 += 2U)
{
one_over_x_pow_two_n_minus_one *= one_over_x2;
const floating_point_type term =
(one_over_x_pow_two_n_minus_one * bernoulli_table()[n2])
/ static_cast<std::uint64_t>(static_cast<std::uint64_t>(n2) * static_cast<std::uint32_t>(n2 - 1U));
using std::fabs;
if((n2 > 6U) && (fabs(term) < tol)) // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
{
break;
}
sum += term;
}
using example008_bernoulli::pi;
using std::exp;
static const floating_point_type half = floating_point_type(1U) / 2U;
static const floating_point_type half_ln_two_pi = log(pi<floating_point_type>() * 2U) / 2U;
floating_point_type g = exp(((((xx - half) * log(xx)) - xx) + half_ln_two_pi) + sum);
// Rescale the result using downward recursion if necessary.
for(auto k = static_cast<std::uint32_t>(0U); k < n_recur; ++k)
{
g /= --xx;
}
return g;
}
} // namespace example008_bernoulli
auto example008_bernoulli_tgamma() -> bool
{
const std::clock_t start = std::clock();
// Initialize the table of Bernoulli numbers.
example008_bernoulli::bernoulli_b
(
example008_bernoulli::bernoulli_table().data(),
static_cast<std::uint32_t>(example008_bernoulli::bernoulli_table().size())
);
// In this example, we compute values of Gamma[1/2 + n].
// We will make use of the relation
// (2n)!
// Gamma[1/2 + n] = -------- * Sqrt[Pi].
// (4^n) n!
// Table[Factorial[2 n]/((4^n) Factorial[n]), {n, 0, 17, 1}]
const std::array<std::pair<std::uint64_t, std::uint32_t>, 18U> ratios =
{{
{ UINT64_C( 1), UINT32_C( 1) },
{ UINT64_C( 1), UINT32_C( 2) },
{ UINT64_C( 3), UINT32_C( 4) },
{ UINT64_C( 15), UINT32_C( 8) },
{ UINT64_C( 105), UINT32_C( 16) },
{ UINT64_C( 945), UINT32_C( 32) },
{ UINT64_C( 10395), UINT32_C( 64) },
{ UINT64_C( 135135), UINT32_C( 128) },
{ UINT64_C( 2027025), UINT32_C( 256) },
{ UINT64_C( 34459425), UINT32_C( 512) },
{ UINT64_C( 654729075), UINT32_C( 1024) },
{ UINT64_C( 13749310575), UINT32_C( 2048) },
{ UINT64_C( 316234143225), UINT32_C( 4096) },
{ UINT64_C( 7905853580625), UINT32_C( 8192) },
{ UINT64_C( 213458046676875), UINT32_C( 16384) },
{ UINT64_C( 6190283353629375), UINT32_C( 32768) },
{ UINT64_C( 191898783962510625), UINT32_C( 65536) },
{ UINT64_C(6332659870762850625), UINT32_C(131072) }
}};
bool result_is_ok = true;
using example008_bernoulli::wide_float_type;
const wide_float_type tol (std::numeric_limits<wide_float_type>::epsilon() * UINT32_C(100000));
const wide_float_type half(0.5F);
for(auto i = static_cast<std::size_t>(0U); i < ratios.size(); ++i)
{
// Calculate Gamma[1/2 + i]
const wide_float_type g = example008_bernoulli::tgamma(half + i);
// Calculate the control value.
using example008_bernoulli::pi;
using std::fabs;
using std::sqrt;
const wide_float_type control = (sqrt(pi<wide_float_type>()) * ratios[i].first) / ratios[i].second; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
const wide_float_type closeness = fabs(1 - (g / control));
result_is_ok &= (closeness < tol);
}
const std::clock_t stop = std::clock();
std::cout << "Time example008_bernoulli_tgamma(): "
<< static_cast<float>(stop - start) / static_cast<float>(CLOCKS_PER_SEC)
<< std::endl;
return result_is_ok;
}
int main()
{
const bool result_is_ok = example008_bernoulli_tgamma();
std::cout << "result_is_ok: " << std::boolalpha << result_is_ok << std::endl;
}
|