diff --git a/include/bi.hpp b/include/bi.hpp index fd5aa1b..ad4886b 100644 --- a/include/bi.hpp +++ b/include/bi.hpp @@ -139,6 +139,8 @@ class BI_API bi_t { int sign() const noexcept; bool odd() const noexcept; bool even() const noexcept; + template + bool within() const noexcept; // Friends BI_API friend std::ostream& operator<<(std::ostream& os, const bi_t& x); diff --git a/src/bi.cpp b/src/bi.cpp index 07a07e9..6760c61 100644 --- a/src/bi.cpp +++ b/src/bi.cpp @@ -813,6 +813,28 @@ std::string bi_t::to_string() const { * @complexity O(1) */ +/** + * @brief Return `true` if this integer fits in an integral type T, `false` + * otherwise. + * + * For `bi_t` object `x` and integral type `T`, `x.within()` evaluates to + * @code + * x >= std::numeric_limits::min() && x <= std::numeric_limits::max() + * @endcode + * + * Example: + * @code{.cpp} + * bi_t x = std::numeric_limits::max(); + * bool fits_in_int32 = x.within(); // true + * bool fits_in_int16 = x.within(); // false + * @endcode + */ +template +bool bi_t::within() const noexcept { + return (*this) >= std::numeric_limits::min() && + (*this) <= std::numeric_limits::max(); +} + ///@} /** @@ -866,12 +888,16 @@ bi_t abs(const bi_t& value) { return value; } +/// @cond BI_INST_TEMPLATE_FOR_INTEGRAL_TYPES(bi_t::bi_t, BI_EMPTY); BI_INST_TEMPLATE_FOR_INTEGRAL_TYPES(bi_t& bi_t::operator=, BI_EMPTY); - BI_INST_TEMPLATE_FOR_INTEGRAL_TYPES(std::strong_ordering bi_t::operator<=>, const noexcept); BI_INST_TEMPLATE_FOR_INTEGRAL_TYPES(bool bi_t::operator==, const noexcept); BI_INST_TEMPLATE_FOR_INTEGRAL_TYPES_CONV(bi_t::operator, const noexcept); + +BI_INST_TEMPLATE_FOR_INTEGRAL_TYPES_NOARG(bool bi_t::within, const noexcept); +/// @endcond + }; // namespace bi diff --git a/src/inst_integral.hpp b/src/inst_integral.hpp index dee4b8b..a9abfc6 100644 --- a/src/inst_integral.hpp +++ b/src/inst_integral.hpp @@ -43,10 +43,15 @@ Extension Signed/Unsigned Integer Types (GCC, Clang) #define BI_INST_TEMPLATE_FOR_EXTENSION_INTEGRAL_TYPES_CONV(FUNC, END) \ template FUNC __int128() END; \ template FUNC unsigned __int128() END; + +#define BI_INST_TEMPLATE_FOR_EXTENSION_INTEGRAL_TYPES_NOARG(FUNC, END) \ + template FUNC<__int128>() END; \ + template FUNC() END; #else #define BI_INST_TEMPLATE_FOR_EXTENSION_INTEGRAL_TYPES(FUNC, END) #define BI_INST_TEMPLATE_FOR_EXTENSION_INTEGRAL_TYPES_ARG(FUNC, ARG) #define BI_INST_TEMPLATE_FOR_EXTENSION_INTEGRAL_TYPES_CONV(FUNC, END) +#define BI_INST_TEMPLATE_FOR_EXTENSION_INTEGRAL_TYPES_NOARG(FUNC, END) #endif #define BI_INST_TEMPLATE_FOR_INTEGRAL_TYPES(FUNC, END) \ @@ -88,6 +93,19 @@ Extension Signed/Unsigned Integer Types (GCC, Clang) template FUNC unsigned long long() END; \ BI_INST_TEMPLATE_FOR_EXTENSION_INTEGRAL_TYPES_CONV(FUNC, END) +#define BI_INST_TEMPLATE_FOR_INTEGRAL_TYPES_NOARG(FUNC, END) \ + template FUNC() END; \ + template FUNC() END; \ + template FUNC() END; \ + template FUNC() END; \ + template FUNC() END; \ + template FUNC() END; \ + template FUNC() END; \ + template FUNC() END; \ + template FUNC() END; \ + template FUNC() END; \ + BI_INST_TEMPLATE_FOR_EXTENSION_INTEGRAL_TYPES_NOARG(FUNC, END) + // NOLINTEND(cppcoreguidelines-macro-usage) #endif // BI_SRC_INST_INTEGRAL_HPP_ diff --git a/test/test.cpp b/test/test.cpp index 1819482..c5211a5 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1700,6 +1700,36 @@ TEST_F(BitwiseOperatorsTest, UnaryComplement) { } } +TEST_F(BITest, WithinIntegral) { + bi_t x; + + EXPECT_TRUE(x.within()); + + x = digit_max; + EXPECT_TRUE(x.within()); + EXPECT_FALSE(x.within()); + + x += 1; + EXPECT_FALSE(x.within()); + + x = -static_cast(digit_max); + EXPECT_FALSE(x.within()); + EXPECT_TRUE(x.within()); + x -= 1; + EXPECT_TRUE(x.within()); + + x = ddigit_max; + EXPECT_TRUE(x.within()); + EXPECT_FALSE(x.within()); + + // From documentation + x = std::numeric_limits::max(); + bool fits_in_int32 = x.within(); // true + bool fits_in_int16 = x.within(); // false + EXPECT_TRUE(fits_in_int32); + EXPECT_FALSE(fits_in_int16); +} + // NOLINTEND(cppcoreguidelines-avoid-magic-numbers) } // namespace