Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
mpreal_ulp.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cstdint>
4#include <limits>
6
7namespace uni20::check::detail
8{
9struct mpreal_ulp_result
10{
11 bool equal = false;
12 long long distance = std::numeric_limits<long long>::max();
13 char const* reason = nullptr;
14};
15
16// Zero has rank zero. Each positive binade contains 2^(p-1) values, with
17// the smallest positive value (exponent emin) at rank one. Negative ranks
18// mirror positive ranks, collapsing the two signed zeros. Storage depends
19// on significand precision, not on the magnitude of the floating exponent.
20inline void mpfr_ulp_key(mpz_ptr key, mpfr_srcptr value, mpfr_exp_t emin)
21{
22 if (mpfr_zero_p(value))
23 {
24 mpz_set_ui(key, 0);
25 return;
26 }
27 uni20::detail::mp_integer significand, minimum;
28 (void)mpfr_get_z_2exp(significand.value, value);
29 mpz_abs(significand.value, significand.value);
30 mpz_set_si(key, mpfr_get_exp(value));
31 mpz_set_si(minimum.value, emin);
32 mpz_sub(key, key, minimum.value);
33 mpz_sub_ui(key, key, 1);
34 mpz_mul_2exp(key, key, static_cast<mp_bitcnt_t>(mpfr_get_prec(value) - 1));
35 mpz_add(key, key, significand.value);
36 mpz_add_ui(key, key, 1);
37 if (mpfr_signbit(value)) mpz_neg(key, key);
38}
39
40inline mpreal_ulp_result compare_mpfr(mpfr_srcptr a, mpfr_srcptr b, std::int64_t max_ulps)
41{
42 if (max_ulps < 0) return {.reason = "negative ULP tolerance"};
43 if (mpfr_get_prec(a) != mpfr_get_prec(b)) return {.reason = "working precisions differ"};
44 if (mpfr_nan_p(a) || mpfr_nan_p(b)) return {.reason = "NaN is not comparable"};
45 if (mpfr_inf_p(a) || mpfr_inf_p(b))
46 {
47 if (mpfr_equal_p(a, b)) return {.equal = true, .distance = 0};
48 return {.reason = "infinities differ or only one operand is infinite"};
49 }
50 auto const emin = mpfr_get_emin(), emax = mpfr_get_emax();
51 auto outside_grid = [=](mpfr_srcptr value) {
52 return !mpfr_zero_p(value) && (mpfr_get_exp(value) < emin || mpfr_get_exp(value) > emax);
53 };
54 if (outside_grid(a) || outside_grid(b)) return {.reason = "operand is outside the current MPFR exponent range"};
55 if (mpfr_equal_p(a, b)) return {.equal = true, .distance = 0};
56
57 uni20::detail::mp_integer left, right, magnitude, limit;
58 mpfr_ulp_key(left.value, a, emin);
59 mpfr_ulp_key(right.value, b, emin);
60 mpz_sub(magnitude.value, right.value, left.value);
61 int const sign = mpz_sgn(magnitude.value);
62 mpz_abs(magnitude.value, magnitude.value);
63 mpz_import(limit.value, 1, 1, sizeof(max_ulps), 0, 0, &max_ulps);
64 bool const equal = mpz_cmp(magnitude.value, limit.value) <= 0;
65
66 // Saturation is only for diagnostics. A distance beyond this range must
67 // not pass a comparison whose tolerance happens to equal the sentinel.
68 constexpr auto maximum = std::numeric_limits<long long>::max();
69 mpz_import(limit.value, 1, 1, sizeof(maximum), 0, 0, &maximum);
70 long long distance = maximum;
71 if (mpz_cmp(magnitude.value, limit.value) < 0)
72 {
73 unsigned long long value = 0;
74 mpz_export(&value, nullptr, 1, sizeof(value), 0, 0, magnitude.value);
75 distance = static_cast<long long>(value);
76 }
77 return {.equal = equal, .distance = sign < 0 ? -distance : distance};
78}
79
80// Rounding an exact operand is local to the assertion. Preserve the caller's
81// exception flags as well as its values, precision, and exponent-range settings.
82struct mpfr_ulp_flags
83{
84 mpfr_flags_t saved = mpfr_flags_save();
85 ~mpfr_ulp_flags() { mpfr_flags_restore(saved, MPFR_FLAGS_ALL); }
86};
87
88inline mpreal_ulp_result compare_mpreal(mpreal const& a, mpreal const& b, std::int64_t max_ulps)
89{
90 mpfr_ulp_flags flags;
91 if (max_ulps < 0) return {.reason = "negative ULP tolerance"};
92 if (!a.initialized() || !b.initialized()) return {.reason = "unset operand"};
93 if (a.is_exact() && b.is_exact())
94 {
95 if (a == b) return {.equal = true, .distance = 0};
96 return {.reason = "unequal exact values have no ULP grid"};
97 }
98 if (a.is_exact())
99 {
100 auto rounded = a.at(b.precision());
101 return compare_mpfr(rounded.native_handle(), b.native_handle(), max_ulps);
102 }
103 if (b.is_exact())
104 {
105 auto rounded = b.at(a.precision());
106 return compare_mpfr(a.native_handle(), rounded.native_handle(), max_ulps);
107 }
108 return compare_mpfr(a.native_handle(), b.native_handle(), max_ulps);
109}
110} // namespace uni20::check::detail
Owning exact rational or finite-precision MPFR real, with eager arithmetic.
Definition mpreal.hpp:74
locale_t value
Definition data_table.cpp:49