std::numeric_limits Support
<beman/big_int/limits.hpp> specializes std::numeric_limits for every
basic_big_int, so generic code can query the type the same way it queries
int or double. Include it directly, or transitively through the umbrella
<beman/big_int.hpp>.
basic_big_int is unbounded: it grows to fit its value, so it has no largest
or smallest value for the specialization to report. Every value-returning
member yields zero, is_bounded is false, and digits saturates at
INT_MAX. These are the same answers
Boost.Multiprecision
gives for an unbounded boost::multiprecision::cpp_int, digit counts included,
which makes the two interchangeable in code that reads these traits.
|
A zero from |
Synopsis
namespace std {
template <std::size_t b, class L, class A>
class numeric_limits<beman::big_int::basic_big_int<b, L, A>> {
using type = beman::big_int::basic_big_int<b, L, A>;
public:
static constexpr bool is_specialized = true;
static constexpr bool is_signed = true;
static constexpr bool is_integer = true;
static constexpr bool is_exact = true;
static constexpr bool has_infinity = false;
static constexpr bool has_quiet_NaN = false;
static constexpr bool has_signaling_NaN = false;
static constexpr float_denorm_style has_denorm = denorm_absent; // deprecated in C++23
static constexpr bool has_denorm_loss = false; // deprecated in C++23
static constexpr float_round_style round_style = round_toward_zero;
static constexpr bool is_iec559 = false;
static constexpr bool is_bounded = false;
static constexpr bool is_modulo = false;
static constexpr int digits = INT_MAX;
static constexpr int digits10 = /* see below */;
static constexpr int max_digits10 = /* see below */;
static constexpr int radix = 2;
static constexpr int min_exponent = 0;
static constexpr int min_exponent10 = 0;
static constexpr int max_exponent = 0;
static constexpr int max_exponent10 = 0;
static constexpr bool traps = false;
static constexpr bool tinyness_before = false;
static constexpr type min() noexcept(/* see below */);
static constexpr type lowest() noexcept(/* see below */);
static constexpr type max() noexcept(/* see below */);
static constexpr type epsilon() noexcept(/* see below */);
static constexpr type round_error() noexcept(/* see below */);
static constexpr type infinity() noexcept(/* see below */);
static constexpr type quiet_NaN() noexcept(/* see below */);
static constexpr type signaling_NaN() noexcept(/* see below */);
static constexpr type denorm_min() noexcept(/* see below */);
};
} // namespace std
The <limits> forwarders for cv-qualified types apply as usual, so
std::numeric_limits<const basic_big_int<…>> reports the same values.
What each member reports
| Member | Value | Notes |
|---|---|---|
|
|
The specialization exists for every |
|
|
|
|
|
- |
|
|
Every value is represented exactly; there is no rounding. |
|
|
There are no such values. |
|
|
Deprecated in C++23; reported for completeness. |
|
|
Deprecated in C++23; reported for completeness. |
|
|
Division truncates toward zero, matching |
|
|
Not a floating-point type. |
|
|
The set of representable values is not finite for any practical purpose. See below. |
|
|
Arithmetic never wraps: an operation that would overflow a fixed-width type grows the representation instead. |
|
|
The count of radix-2 digits is not fixed, so it saturates rather than reporting a width. |
|
|
|
|
|
|
|
|
The magnitude is a sequence of binary limbs. |
|
|
Not a floating-point type. |
|
|
No operation traps. |
|
|
Not a floating-point type. |
|
|
There is no extreme value. See the note above. |
|
|
Not meaningful for an exact integer type. |
|
|
No such value exists, so the default is returned. |
The digit counts are derived from digits with the same expressions
Boost.Multiprecision uses, down to the shared log10(2) constant, so the two
libraries report identical numbers. The values quoted above are what those
expressions produce wherever INT_MAX is 2147483647.
The noexcept specifications
Each value-returning member returns a default-constructed basic_big_int, and
is noexcept exactly when that constructor is — that is, when the allocator’s
default constructor is noexcept. This holds for big_int and for
pmr::big_int, so in practice every member is noexcept; only a user-supplied
allocator with a throwing default constructor makes it otherwise.
All members are usable in a constant expression.
Why is_bounded is false
A basic_big_int object does have a ceiling: its representation cannot exceed
max_size() bits, reported per object by
max_size and
max_representation_size. That ceiling is a property of the representation
rather than of the type, and it sits far beyond any allocation a program can
make — more than 1011 bits on a 64-bit platform, which is why an allocation
failure, not the limit, is what a program actually meets first.
Reporting it as max() would therefore be misleading twice over: it would
promise a value no program can construct, and it would let a template silently
treat basic_big_int as a fixed-width type. is_bounded == false says the
useful thing instead, and matches what Boost.Multiprecision reports for
cpp_int.
Using it in generic code
The specialization is there so that a template constrained on
std::numeric_limits accepts basic_big_int alongside the built-in integers:
template <class T>
concept exact_integer = std::numeric_limits<T>::is_specialized &&
std::numeric_limits<T>::is_integer &&
std::numeric_limits<T>::is_exact;
static_assert(exact_integer<beman::big_int::big_int>);
static_assert(exact_integer<int>);
static_assert(!exact_integer<double>);
Code that needs a bound must branch on is_bounded:
template <class T>
std::string describe_range() {
using lim = std::numeric_limits<T>;
if constexpr (lim::is_bounded) {
return std::to_string((lim::min)()) + " .. " + std::to_string((lim::max)());
} else {
return "unbounded";
}
}
See the numeric limits example for a complete program.