Skip to content

Examples

Howard Hinnant edited this page Sep 8, 2025 · 4 revisions

Here are some examples of what this library can do.


Calculate pi to 100 digits using continued fractions:

#include "bbi.h"
#include <array>
#include <iostream>
#include <string>

template <class Int>
Int
power(Int const& f, unsigned n)
{
    if (n == 0)
        return 1;
    if (n == 1)
        return f;
    auto r = power(f, n / 2);
    r *= r;
    if (n % 2 != 0)
        r *= f;
    return r;
}

template <class Int>
Int
log10(Int const& f)
{
    return Int{std::string{f}.size()};
}

int constexpr pi_a[] =
{
    3,   7,  15,   1, 292,   1,   1,   1,   2,   1,
    3,   1,  14,   2,   1,   1,   2,   2,   2,   2,
    1,  84,   2,   1,   1,  15,   3,  13,   1,   4,
    2,   6,   6,  99,   1,   2,   2,   6,   3,   5,
    1,   1,   6,   8,   1,   7,   1,   2,   3,   7,
    1,   2,   1,   1,  12,   1,   1,   1,   3,   1,
    1,   8,   1,   1,   2,   1,   6,   1,   1,   5,
    2,   2,   3,   1,   2,   4,   4,  16,   1, 161,
   45,   1,   22,  1,   2,   2,   1,   4,   1,   2,
   24,   1,    2,  1,   3,   1,   2,   1,   1,  10,
    2,   5
};

constexpr unsigned Npi = sizeof(pi_a)/sizeof(pi_a[0]);

template <class Int>
constexpr
std::array<Int, Npi>
make_pi_d()
{
    std::array<Int, Npi> pi_d{1, 7};
    for (auto i = 2u; i < Npi; ++i)
        pi_d[i] = pi_a[i] * pi_d[i-1] + pi_d[i-2];
    return pi_d;
}

template <class Int>
Int
pi_d(int n)
{
    static auto const den = make_pi_d<Int>();
    return den[n];
}

template <class Int>
constexpr
std::array<Int, Npi>
make_pi_n()
{
    std::array<Int, Npi> pi_n{3, 22};
    for (auto i = 2u; i < Npi; ++i)
        pi_n[i] = pi_a[i] * pi_n[i-1] + pi_n[i-2];
    return pi_n;
}

template <class Int>
Int
pi_n(int n)
{
    static auto const num = make_pi_n<Int>();
    return num[n];
}

int
main()
{
    using namespace bbi::term;
    auto pi_num = pi_n<i256>(Npi-1);
    auto pi_den = pi_d<i256>(Npi-1);
    auto f = power(i1024{10}, unsigned{log10(pi_num)+log10(pi_den)-2});
    auto d = pi_num * f / pi_den;
    std::string s{d};
    s.insert(s.begin()+1, '.');
    std::cout << pi_num << " / " << pi_den << " = " << s << '\n';
}

Output:

47851633293495758577879878030394929021214201883870831 / 15231647947361123271599045989184750211511930092111565 = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214

I used i256 for the numerators and denominators computed from the continued fraction representation. And then to compute the fraction I used a i1024. And I know no overflow occurred because if it had, std::terminate would have been called. I was able to experimentally determine that these were the smallest types I could use for each computation, as using even smaller types resulted in a call to std::terminate.

This example demonstrates the ease with which bbi types can use built-in integral types, and the ease with which different sizes of bbi types can interoperate. Because all implicit conversions are value-preserving, and because all overflows are caught with terminate (in namespace bbi::term), I have high confidence that this computation is free of run-time errors.

Clone this wiki locally