File: best_rational_approx.h

package info (click to toggle)
dragonbox 1.1.3-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 5,148 kB
  • sloc: cpp: 8,752; ansic: 1,522; makefile: 18
file content (97 lines) | stat: -rw-r--r-- 3,902 bytes parent folder | download | duplicates (5)
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
// Copyright 2022 Junekey Jeon
//
// The contents of this file may be used under the terms of
// the Apache License v2.0 with LLVM Exceptions.
//
//    (See accompanying file LICENSE-Apache or copy at
//     https://llvm.org/foundation/relicensing/LICENSE.txt)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE-Boost or copy at
//     https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.

#ifndef JKJ_HEADER_BEST_RATIONAL_APPROX
#define JKJ_HEADER_BEST_RATIONAL_APPROX

#include "continued_fractions.h"
#include <cassert>
#include <cstdlib>

namespace jkj {
    template <class UInt>
    struct best_rational_approx_output {
        unsigned_rational<UInt> below;
        unsigned_rational<UInt> above;
    };

    // Find the best rational approximations from below and from above of denominators no more than
    // denominator_upper_bound for the given number x.
    template <class ContinuedFractionsImpl, class UInt, class PositiveNumber>
    best_rational_approx_output<typename ContinuedFractionsImpl::uint_type>
    find_best_rational_approx(PositiveNumber const& x, UInt const& denominator_upper_bound) {
        assert(denominator_upper_bound > 0);

        using uint_type = typename ContinuedFractionsImpl::uint_type;
        best_rational_approx_output<uint_type> ret_value;

        // Initialize a continued fractions calculator.
        ContinuedFractionsImpl cf{x};

        // First, find the last convergent whose denominator is bounded above by the given upper
        // bound.
        unsigned_rational<uint_type> previous_convergent;
        unsigned_rational<uint_type> current_convergent;
        do {
            previous_convergent = cf.previous_convergent();
            current_convergent = cf.current_convergent();

            // Obtain the next convergent.
            if (!cf.update()) {
                // If there is no more convergents, we already obtained the perfect approximation.
                ret_value.below = cf.current_convergent();
                ret_value.above = cf.current_convergent();
                return ret_value;
            }
        } while (cf.current_denominator() <= denominator_upper_bound);

        // If the current convergent is of even index,
        // then the current convergent is the best approximation from below,
        // and the best approximation from above is the last semiconvergent.
        // If the current convergent is of odd index, then the other way around.
        // Note that cf.current_index() is one larger than the index of the current convergent,
        // so we need to reverse the parity.

        auto compute_bounds = [&](auto& major, auto& minor) {
            // The current convergent is the best approximation from below.
            major = current_convergent;

            // The best approximation from above is the last semiconvergent.
            using std::div;
            auto semiconvergent_coeff =
                div(denominator_upper_bound - previous_convergent.denominator,
                    current_convergent.denominator)
                    .quot;

            minor.numerator =
                previous_convergent.numerator + semiconvergent_coeff * current_convergent.numerator;
            minor.denominator = previous_convergent.denominator +
                                semiconvergent_coeff * current_convergent.denominator;
        };

        if (cf.current_index() % 2 == 1) {
            compute_bounds(ret_value.below, ret_value.above);
        }
        else {
            compute_bounds(ret_value.above, ret_value.below);
        }

        return ret_value;
    }
}

#endif