File: verify_fast_multiplication.cpp

package info (click to toggle)
dragonbox 1.1.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,148 kB
  • sloc: cpp: 8,752; ansic: 1,522; makefile: 18
file content (168 lines) | stat: -rw-r--r-- 6,363 bytes parent folder | download | duplicates (2)
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
// Copyright 2020-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.

#include "dragonbox/dragonbox.h"
#include "big_uint.h"
#include "continued_fractions.h"

#include <iostream>

template <class Float, class CachePolicy>
static bool verify_fast_multiplication_xz(CachePolicy&& cache_policy) {
    using impl = jkj::dragonbox::detail::impl<Float>;
    using format = typename impl::format;
    using carrier_uint = typename impl::carrier_uint;

    constexpr auto four_fl = (carrier_uint(1) << (impl::significand_bits + 2)) - 1;
    constexpr auto two_fr = (carrier_uint(1) << (impl::significand_bits + 1)) + 1;

    using jkj::dragonbox::detail::log::floor_log10_pow2_minus_log10_4_over_3;
    using jkj::dragonbox::detail::log::floor_log2_pow10;

    bool success = true;

    for (int e = impl::min_exponent + 1 - impl::significand_bits;
         e <= impl::max_exponent - impl::significand_bits; ++e) {

        // Compute k and beta.
        int const k = -floor_log10_pow2_minus_log10_4_over_3(e);
        int const beta = e + floor_log2_pow10(k);

        // Load cache.
        auto const cache = cache_policy.template get_cache<format>(k);

        // Compute the endpoints using the fast method.
        auto x_fast = impl::compute_left_endpoint_for_shorter_interval_case(cache, beta);
        auto z_fast = impl::compute_right_endpoint_for_shorter_interval_case(cache, beta);

        // Precisely compute the endpoints.
        jkj::unsigned_rational<jkj::big_uint> precise_multiplier{1, 1};
        if (k >= 0) {
            precise_multiplier.numerator = jkj::big_uint::pow(5, k);
        }
        else {
            precise_multiplier.denominator = jkj::big_uint::pow(5, -k);
        }
        if (e + k >= 0) {
            precise_multiplier.numerator *= jkj::big_uint::power_of_2(e + k);
        }
        else {
            precise_multiplier.denominator *= jkj::big_uint::power_of_2(-e - k);
        }

        auto x_exact =
            (four_fl * precise_multiplier.numerator) / (4 * precise_multiplier.denominator);
        auto z_exact =
            (two_fr * precise_multiplier.numerator) / (2 * precise_multiplier.denominator);

        if (x_exact != x_fast) {
            std::cout << "(e = " << e << ") left endpoint is not correct; computed = " << x_fast
                      << "; true_value = " << x_exact[0] << "\n";
            success = false;
        }
        if (z_exact != z_fast) {
            std::cout << "(e = " << e << ") right endpoint is not correct; computed = " << z_fast
                      << "; true_value = " << z_exact[0] << "\n";
            success = false;
        }
    }

    if (success) {
        std::cout << "All cases are verified.\n";
    }
    else {
        std::cout << "Error detected.\n";
    }

    return success;
}

template <class Float, class CachePolicy>
static bool verify_fast_multiplication_yru(CachePolicy&& cache_policy) {
    using impl = jkj::dragonbox::detail::impl<Float>;
    using format = typename impl::format;
    bool success = true;

    for (int k = impl::min_k; k <= impl::max_k; ++k) {
        auto const cache = cache_policy.template get_cache<format>(k);

        // Since Q - p - beta - 2 >= q, it suffices to check that the lower half of the cache is not 0.
        auto const lower_half = [cache] {
            if constexpr (std::is_same_v<typename impl::format, jkj::dragonbox::ieee754_binary32>) {
                return std::uint32_t(cache);
            }
            else {
                return cache.low();
            }
        }();

        // If the lower half is zero, we need to check if the cache is precise.
        if (lower_half == 0) {
            if (k < 0 || k > jkj::dragonbox::detail::log::floor_log5_pow2(impl::cache_bits)) {
                std::cout << "(k = " << k << ") computation might be incorrect\n";
                success = false;
            }
        }
    }

    if (success) {
        std::cout << "All cases are verified.\n";
    }
    else {
        std::cout << "Error detected.\n";
    }

    return success;
}

int main() {
    bool success = true;

    std::cout << "[Verifying fast computation of xi and zi for the shorter interval case "
                 "with full cache (binary32)...]\n";
    success &= verify_fast_multiplication_xz<float>(jkj::dragonbox::policy::cache::full);
    std::cout << "Done.\n\n\n";

    std::cout << "[Verifying fast computation of yru for the shorter interval case "
                 "with full cache (binary32)...]\n";
    success &= verify_fast_multiplication_yru<float>(jkj::dragonbox::policy::cache::full);
    std::cout << "Done.\n\n\n";

    std::cout << "[Verifying fast computation of xi and zi for the shorter interval case "
                 "with full cache (binary64)...]\n";
    success &= verify_fast_multiplication_xz<double>(jkj::dragonbox::policy::cache::full);
    std::cout << "Done.\n\n\n";

    std::cout << "[Verifying fast computation of xi and zi for the shorter interval case "
                 "with compressed cache (binary64)...]\n";
    success &= verify_fast_multiplication_xz<double>(jkj::dragonbox::policy::cache::compact);
    std::cout << "Done.\n\n\n";

    std::cout << "[Verifying fast computation of yru for the shorter interval case "
                 "with full cache (binary64)...]\n";
    success &= verify_fast_multiplication_yru<double>(jkj::dragonbox::policy::cache::full);
    std::cout << "Done.\n\n\n";

    std::cout << "[Verifying fast computation of yru for the shorter interval case "
                 "with compressed cache (binary64)...]\n";
    success &= verify_fast_multiplication_yru<double>(jkj::dragonbox::policy::cache::compact);
    std::cout << "Done.\n\n\n";

    if (!success) {
        return -1;
    }
}