From a58b14146784e2a496bec9f621c6a9c8c6a5815f Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 19 Feb 2026 18:03:10 -0500 Subject: [PATCH 1/2] Whitespace. --- test/math/addition.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/math/addition.cpp b/test/math/addition.cpp index 3ea0625df9..f124b6f7a9 100644 --- a/test/math/addition.cpp +++ b/test/math/addition.cpp @@ -18,7 +18,7 @@ */ #include "../test.hpp" - // creates addition_tests namespace +// creates addition_tests namespace BOOST_AUTO_TEST_SUITE(addition_tests) constexpr uint32_t pos_uint32 = 42; From c8362a3d7fbad3033dae8afcbaa9aefab53f5435 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 19 Feb 2026 18:04:58 -0500 Subject: [PATCH 2/2] Expand float to integer casting. --- Makefile.am | 1 + builds/cmake/CMakeLists.txt | 1 + .../libbitcoin-system-test.vcxproj | 1 + .../libbitcoin-system-test.vcxproj.filters | 3 + include/bitcoin/system/constraints.hpp | 2 + include/bitcoin/system/impl/math/cast.ipp | 54 ++- include/bitcoin/system/math/cast.hpp | 42 +- test/math/cast.cpp | 438 +++++++----------- test/math/expectations.cpp | 298 ++++++++++++ 9 files changed, 551 insertions(+), 289 deletions(-) create mode 100644 test/math/expectations.cpp diff --git a/Makefile.am b/Makefile.am index ead0a906a2..cbd9eab2d0 100755 --- a/Makefile.am +++ b/Makefile.am @@ -349,6 +349,7 @@ test_libbitcoin_system_test_SOURCES = \ test/math/byteswap.cpp \ test/math/cast.cpp \ test/math/division.cpp \ + test/math/expectations.cpp \ test/math/limits.cpp \ test/math/logarithm.cpp \ test/math/multiplication.cpp \ diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index 0d09958a8c..3a4185e353 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -828,6 +828,7 @@ if (with-tests) "../../test/math/byteswap.cpp" "../../test/math/cast.cpp" "../../test/math/division.cpp" + "../../test/math/expectations.cpp" "../../test/math/limits.cpp" "../../test/math/logarithm.cpp" "../../test/math/multiplication.cpp" diff --git a/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj b/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj index 0ff280e3ba..cbdfbdb00e 100644 --- a/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj @@ -275,6 +275,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj.filters index 55ea6c40bb..5f4d08ba0f 100644 --- a/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj.filters @@ -477,6 +477,9 @@ src\math + + src\math + src\math diff --git a/include/bitcoin/system/constraints.hpp b/include/bitcoin/system/constraints.hpp index f6e8abac77..e20a7aed3f 100644 --- a/include/bitcoin/system/constraints.hpp +++ b/include/bitcoin/system/constraints.hpp @@ -255,6 +255,8 @@ using if_little_endian_integral_integer = bool_if< is_integral_integer && is_little_endian>; +/// Floating pont types (native, floating point, non-bool). + template using if_floating_point = bool_if< is_floating_point>; diff --git a/include/bitcoin/system/impl/math/cast.ipp b/include/bitcoin/system/impl/math/cast.ipp index e627e9aac7..d6f8d13756 100644 --- a/include/bitcoin/system/impl/math/cast.ipp +++ b/include/bitcoin/system/impl/math/cast.ipp @@ -209,31 +209,71 @@ constexpr Unsigned to_unsigned(Unsigned value) NOEXCEPT // Floating point casts. // ---------------------------------------------------------------------------- +// inlines below be not constant until c++23. template , if_floating_point> -constexpr bool to_integer(Integer& out, Float value) NOEXCEPT +inline bool to_integer(Integer& out, Float value, bool whole) NOEXCEPT { if (!std::isfinite(value)) return false; + // TODO: Could use tolerance for fp epsilon to handle representation error. Float integer{}; - const Float fractional = std::modf(value, &integer); - if (fractional != 0.0) + if ((std::modf(value, &integer) != 0.0) && whole) return false; if (integer > static_cast(std::numeric_limits::max()) || integer < static_cast(std::numeric_limits::min())) return false; - // Floating point conversion in c++ requires explicit or implicit cast. - BC_PUSH_WARNING(NO_CASTS_FOR_ARITHMETIC_CONVERSION) - out = static_cast(integer); - BC_POP_WARNING() + out = to_integer(integer); return true; } +template , + if_floating_point> +inline Integer to_ceilinged_integer(Float value) NOEXCEPT +{ + return to_truncated_integer(std::ceil(value)); +} + +template , + if_floating_point> +inline Integer to_floored_integer(Float value) NOEXCEPT +{ + return to_truncated_integer(std::floor(value)); +} + +template , + if_floating_point> +inline Integer to_rounded_integer(Float value) NOEXCEPT +{ + return to_truncated_integer(std::round(value)); +} + +template , + if_floating_point> +inline Integer to_truncated_integer(Float value) NOEXCEPT +{ + if (!std::isfinite(value)) + return std::numeric_limits::max(); + + const auto integer = std::trunc(value); + if (integer > static_cast(std::numeric_limits::max())) + return std::numeric_limits::max(); + + if (integer < static_cast(std::numeric_limits::min())) + return std::numeric_limits::min(); + + return to_integer(integer); +} + template , if_floating_point> diff --git a/include/bitcoin/system/math/cast.hpp b/include/bitcoin/system/math/cast.hpp index 4e44d64747..3a680ecf56 100644 --- a/include/bitcoin/system/math/cast.hpp +++ b/include/bitcoin/system/math/cast.hpp @@ -142,19 +142,53 @@ constexpr Unsigned to_unsigned(Unsigned value) NOEXCEPT; /// Floating point casts. /// --------------------------------------------------------------------------- -/// Cast floating point to integral integer, overflow guarded. +/// Cast floating point to integral integer (truncated). +/// True for any finite value that falls within Integer domain. +/// False if fractional part is non-zero when whole is set true. template = true, if_floating_point = true> -constexpr bool to_integer(Integer& out, Float value) NOEXCEPT; +inline bool to_integer(Integer& out, Float value, bool whole=true) NOEXCEPT; -/// Cast floating point to integral integer, overflow unguarded. +/// Cast floating point to integral integer (ceilinged). +/// Any non-finite value returns the integer domain maximum. +/// Overflow/underflow is clamped to respective integer upper/lower limit. +template = true, + if_floating_point = true> +inline Integer to_ceilinged_integer(Float value) NOEXCEPT; + +/// Cast floating point to integral integer (floored). +/// Any non-finite value returns the integer domain maximum. +/// Overflow/underflow is clamped to respective integer upper/lower limit. +template = true, + if_floating_point = true> +inline Integer to_floored_integer(Float value) NOEXCEPT; + +/// Cast floating point to integral integer (rounded). +/// Any non-finite value returns the integer domain maximum. +/// Overflow/underflow is clamped to respective integer upper/lower limit. +template = true, + if_floating_point = true> +inline Integer to_rounded_integer(Float value) NOEXCEPT; + +/// Cast floating point to integral integer (truncated). +/// Any non-finite value returns the integer domain maximum. +/// Overflow/underflow is clamped to respective integer upper/lower limit. +template = true, + if_floating_point = true> +inline Integer to_truncated_integer(Float value) NOEXCEPT; + +/// Cast floating point to integral integer, overflow/underflow UNGUARDED. template = true, if_floating_point = true> constexpr Integer to_integer(Float value) NOEXCEPT; -/// Cast integral integer to floating point, overflow unguarded. +/// Cast integral integer to floating point, overflow/underflow not possible. template = true, if_integral_integer = true> diff --git a/test/math/cast.cpp b/test/math/cast.cpp index 7ef6fc9504..e93b051791 100644 --- a/test/math/cast.cpp +++ b/test/math/cast.cpp @@ -17,6 +17,7 @@ * along with this program. If not, see . */ #include "../test.hpp" +#include // promote // depromote @@ -76,281 +77,162 @@ static_assert(is_same_type); static_assert(is_same_type(1u)), float>); static_assert(is_same_type(2u)), double>); -// Verify compiler "usual arithmetic conversion" expectations. -// ---------------------------------------------------------------------------- -// Shift works like a unary op (right operand is not incorporated). -// Order of operands does not affect (other) binary ops. -// self-assigning (e.g. >>=, /=) ops obviously do not (cannot) promote. -// ! and comparison are not mathematical ops. - -// Unary ops (+/-/~) change to unsigned upon width promotion, not otherwise. -// + is a unary op and produces promotion. -// std::common_type does not support unary operators. -// std::common_type::type == decltype(left - right) -// std::common_type::type != decltype(-value) -using integer = decltype(-'a'); -using character = std::common_type_t; -using common_character = std::common_type_t; -static_assert(!is_same_type); -static_assert(!is_same_type); - -// Compiler warns on bool safety for -b, ~b, <>b, /b, %b, b/, b% (assertions disabled). - -// invert -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); - -// comparison -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type= uint16_t{0}), bool>, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); -static_assert(is_same_type, "boolean"); - -// positive -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "unpromoted (>=32"); -static_assert(is_same_type, "unpromoted (>=32"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "unpromoted (>=32"); -static_assert(is_same_type, "unpromoted (>=32"); - -// negate (signed) -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "unpromoted (>=32"); -static_assert(is_same_type, "unpromoted (>=32"); -////static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -////static_assert(is_same_type, "unpromoted (>=32"); -////static_assert(is_same_type, "unpromoted (>=32"); - -// negate (unsigned) -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "unpromoted (>=32"); -static_assert(is_same_type, "unpromoted (>=32"); -////static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -////static_assert(is_same_type, "unpromoted (>=32"); -////static_assert(is_same_type, "unpromoted (>=32"); - -// ones_complement -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "unpromoted (>=32"); -static_assert(is_same_type, "unpromoted (>=32"); -////static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "unpromoted (>=32"); -static_assert(is_same_type, "unpromoted (>=32"); - -// shift_left -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "unpromoted (>=32"); -static_assert(is_same_type, "unpromoted (>=32"); -////static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "promoted (<32)"); -static_assert(is_same_type, "unpromoted (>=32"); -static_assert(is_same_type, "unpromoted (>=32"); - -// shift_right -static_assert(is_same_type> char{0}), int>, "promoted (<32)"); -static_assert(is_same_type> uint8_t{0}), int>, "promoted (<32)"); -static_assert(is_same_type> uint16_t{0}), int>, "promoted (<32)"); -static_assert(is_same_type> uint32_t{0}), int >, "promoted (<32)"); -static_assert(is_same_type> uint64_t{0}), uint32_t>, "unpromoted (>=32"); -static_assert(is_same_type> uint64_t{0}), uint64_t>, "unpromoted (>=32"); -////static_assert(is_same_type> bool{false}), int>, "promoted (<32)"); -static_assert(is_same_type> uint32_t{0}), int>, "promoted (<32)"); -static_assert(is_same_type> int8_t{0}), int>, "promoted (<32)"); -static_assert(is_same_type> int16_t{0}), int>, "promoted (<32)"); -static_assert(is_same_type> int32_t{0}), int>, "promoted (<32)"); -static_assert(is_same_type> int64_t{0}), uint32_t>, "unpromoted (>=32"); -static_assert(is_same_type> uint32_t{0}), int64_t>, "unpromoted (>=32"); - -// bit_and -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "unsigned (two >=32, s/u)"); -static_assert(is_same_type, "unsigned (two >=32, u/s)"); - -// bit_or -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "unsigned (two >=32, s/u)"); -static_assert(is_same_type, "unsigned (two >=32, u/s)"); - -// bit_xor -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "unsigned (two >=32, s/u)"); -static_assert(is_same_type, "unsigned (two >=32, u/s)"); - -// add -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "unsigned (two >=32, s/u)"); -static_assert(is_same_type, "unsigned (two >=32, u/s)"); - -// subtract -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "unsigned (two >=32, s/u)"); -static_assert(is_same_type, "unsigned (two >=32, u/s)"); - -// subtract (reversed) -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "unsigned (two >=32, s-u)"); -static_assert(is_same_type, "unsigned (two >=32, u-s)"); - -// multiply -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "unsigned (two >=32, s/u)"); -static_assert(is_same_type, "unsigned (two >=32, u/s)"); - -// divide -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -////static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "unsigned (two >=32, s/u)"); -static_assert(is_same_type, "unsigned (two >=32, u/s)"); - -// divide (reversed) -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -////static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "unsigned (two >=32, s/u)"); -static_assert(is_same_type, "unsigned (two >=32, u/s)"); - -// modulo -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -////static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "unsigned (two >=32, s%u)"); -static_assert(is_same_type, "unsigned (two >=32, u%s)"); - -// modulo (reversed) -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -static_assert(is_same_type, "widest (two >=32)"); -////static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "promoted (two <32)"); -static_assert(is_same_type, "widest (one >=32)"); -static_assert(is_same_type, "unsigned (two >=32, s%u)"); -static_assert(is_same_type, "unsigned (two >=32, u%s)"); +// TODO: below can move to static_assert under c++23. + +BOOST_AUTO_TEST_SUITE(cast_tests) + +template +using limit = std::numeric_limits; + +// to_integer2/3 + +BOOST_AUTO_TEST_CASE(cast__to_integer__non_finites__false) +{ + size_t value{}; + BOOST_REQUIRE(!to_integer(value, limit::quiet_NaN())); + BOOST_REQUIRE(!to_integer(value, limit::infinity())); + BOOST_REQUIRE(!to_integer(value, -limit::infinity())); + BOOST_REQUIRE(!to_integer(value, limit::quiet_NaN(), false)); + BOOST_REQUIRE(!to_integer(value, limit::infinity(), false)); + BOOST_REQUIRE(!to_integer(value, -limit::infinity(), false)); +} + +BOOST_AUTO_TEST_CASE(cast__to_integer__finites__expected) +{ + size_t value{}; + BOOST_REQUIRE(to_integer(value, 1.0, true)); + BOOST_REQUIRE_EQUAL(value, 1u); + BOOST_REQUIRE(to_integer(value, 2.0, false)); + BOOST_REQUIRE_EQUAL(value, 2u); + BOOST_REQUIRE(to_integer(value, 3.1, false)); + BOOST_REQUIRE_EQUAL(value, 3u); + BOOST_REQUIRE(!to_integer(value, 3.1, true)); +} + +// to_ceilinged_integer + +BOOST_AUTO_TEST_CASE(cast__to_ceilinged_integer__non_finites__max) +{ + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(limit::quiet_NaN()), max_size_t); + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(limit::infinity()), max_size_t); + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(-limit::infinity()), max_size_t); +} + +BOOST_AUTO_TEST_CASE(cast__to_ceilinged_integer__overflow_underflow__clamped) +{ + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(limit::max()), max_size_t); + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(-limit::max()), min_size_t); + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(limit::max()), max_int64); + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(-limit::max()), min_int64); +} + +BOOST_AUTO_TEST_CASE(cast__to_ceilinged_integer__finites__expected) +{ + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(0.0), 0u); + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(1.0), 1u); + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(1.1), 2u); + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(1.9), 2u); + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(-0.1), 0u); // Ceil to 0 for unsigned + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(-1.1), 0u); // Clamped to min (0) + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(-1.0), -1); + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(-1.1), -1); + BOOST_REQUIRE_EQUAL(to_ceilinged_integer(-1.9), -1); +} + +// to_floored_integer + +BOOST_AUTO_TEST_CASE(cast__to_floored_integer__non_finites__max) +{ + BOOST_REQUIRE_EQUAL(to_floored_integer(limit::quiet_NaN()), max_size_t); + BOOST_REQUIRE_EQUAL(to_floored_integer(limit::infinity()), max_size_t); + BOOST_REQUIRE_EQUAL(to_floored_integer(-limit::infinity()), max_size_t); +} + +BOOST_AUTO_TEST_CASE(cast__to_floored_integer__overflow_underflow__clamped) +{ + BOOST_REQUIRE_EQUAL(to_floored_integer(limit::max()), max_size_t); + BOOST_REQUIRE_EQUAL(to_floored_integer(-limit::max()), min_size_t); + BOOST_REQUIRE_EQUAL(to_floored_integer(limit::max()), max_int64); + BOOST_REQUIRE_EQUAL(to_floored_integer(-limit::max()), min_int64); +} + +BOOST_AUTO_TEST_CASE(cast__to_floored_integer__finites__expected) +{ + BOOST_REQUIRE_EQUAL(to_floored_integer(0.0), 0u); + BOOST_REQUIRE_EQUAL(to_floored_integer(1.0), 1u); + BOOST_REQUIRE_EQUAL(to_floored_integer(1.1), 1u); + BOOST_REQUIRE_EQUAL(to_floored_integer(1.9), 1u); + BOOST_REQUIRE_EQUAL(to_floored_integer(-0.1), 0u); // Floor to -1, but clamped to 0 + BOOST_REQUIRE_EQUAL(to_floored_integer(-1.1), 0u); // Clamped to min (0) + BOOST_REQUIRE_EQUAL(to_floored_integer(-1.0), -1); + BOOST_REQUIRE_EQUAL(to_floored_integer(-1.1), -2); + BOOST_REQUIRE_EQUAL(to_floored_integer(-1.9), -2); +} + +// to_rounded_integer + +BOOST_AUTO_TEST_CASE(cast__to_rounded_integer__non_finites__max) +{ + BOOST_REQUIRE_EQUAL(to_rounded_integer(limit::quiet_NaN()), max_size_t); + BOOST_REQUIRE_EQUAL(to_rounded_integer(limit::infinity()), max_size_t); + BOOST_REQUIRE_EQUAL(to_rounded_integer(-limit::infinity()), max_size_t); +} + +BOOST_AUTO_TEST_CASE(cast__to_rounded_integer__overflow_underflow__clamped) +{ + BOOST_REQUIRE_EQUAL(to_rounded_integer(limit::max()), max_size_t); + BOOST_REQUIRE_EQUAL(to_rounded_integer(-limit::max()), min_size_t); + BOOST_REQUIRE_EQUAL(to_rounded_integer(limit::max()), max_int64); + BOOST_REQUIRE_EQUAL(to_rounded_integer(-limit::max()), min_int64); +} + +BOOST_AUTO_TEST_CASE(cast__to_rounded_integer__finites__expected) +{ + BOOST_REQUIRE_EQUAL(to_rounded_integer(0.0), 0u); + BOOST_REQUIRE_EQUAL(to_rounded_integer(1.0), 1u); + BOOST_REQUIRE_EQUAL(to_rounded_integer(1.1), 1u); + BOOST_REQUIRE_EQUAL(to_rounded_integer(1.4), 1u); + BOOST_REQUIRE_EQUAL(to_rounded_integer(1.5), 2u); + BOOST_REQUIRE_EQUAL(to_rounded_integer(1.6), 2u); + BOOST_REQUIRE_EQUAL(to_rounded_integer(-0.1), 0u); // Round to 0 + BOOST_REQUIRE_EQUAL(to_rounded_integer(-0.5), 0u); // Round to -1, clamped to 0 + BOOST_REQUIRE_EQUAL(to_rounded_integer(-1.1), 0u); // Clamped to min (0) + BOOST_REQUIRE_EQUAL(to_rounded_integer(-1.0), -1); + BOOST_REQUIRE_EQUAL(to_rounded_integer(-1.1), -1); + BOOST_REQUIRE_EQUAL(to_rounded_integer(-1.4), -1); + BOOST_REQUIRE_EQUAL(to_rounded_integer(-1.5), -2); + BOOST_REQUIRE_EQUAL(to_rounded_integer(-1.6), -2); + BOOST_REQUIRE_EQUAL(to_rounded_integer(-2.5), -3); // Halfway away from min_size_t +} + +// to_truncated_integer + +BOOST_AUTO_TEST_CASE(cast__to_truncated_integer__non_finites__max) +{ + BOOST_REQUIRE_EQUAL(to_truncated_integer(limit::quiet_NaN()), max_size_t); + BOOST_REQUIRE_EQUAL(to_truncated_integer(limit::infinity()), max_size_t); + BOOST_REQUIRE_EQUAL(to_truncated_integer(-limit::infinity()), max_size_t); +} + +BOOST_AUTO_TEST_CASE(cast__to_truncated_integer__overflow_underflow__clamped) +{ + BOOST_REQUIRE_EQUAL(to_truncated_integer(limit::max()), max_size_t); + BOOST_REQUIRE_EQUAL(to_truncated_integer(-limit::max()), min_size_t); + BOOST_REQUIRE_EQUAL(to_truncated_integer(limit::max()), max_signed_size_t); + BOOST_REQUIRE_EQUAL(to_truncated_integer(-limit::max()), min_signed_size_t); +} + +BOOST_AUTO_TEST_CASE(cast__to_truncated_integer__finites__expected) +{ + BOOST_REQUIRE_EQUAL(to_truncated_integer(0.0), 0u); + BOOST_REQUIRE_EQUAL(to_truncated_integer(1.0), 1u); + BOOST_REQUIRE_EQUAL(to_truncated_integer(1.1), 1u); + BOOST_REQUIRE_EQUAL(to_truncated_integer(1.9), 1u); + BOOST_REQUIRE_EQUAL(to_truncated_integer(-0.1), 0u); // Trunc to 0 + BOOST_REQUIRE_EQUAL(to_truncated_integer(-1.1), 0u); // Clamped to min (0) + BOOST_REQUIRE_EQUAL(to_truncated_integer(-1.0), -1); + BOOST_REQUIRE_EQUAL(to_truncated_integer(-1.1), -1); + BOOST_REQUIRE_EQUAL(to_truncated_integer(-1.9), -1); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/math/expectations.cpp b/test/math/expectations.cpp new file mode 100644 index 0000000000..7ef4dbaf9e --- /dev/null +++ b/test/math/expectations.cpp @@ -0,0 +1,298 @@ +/** + * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "../test.hpp" + +// Verify compiler "usual arithmetic conversion" expectations. +// ---------------------------------------------------------------------------- +// Shift works like a unary op (right operand is not incorporated). +// Order of operands does not affect (other) binary ops. +// self-assigning (e.g. >>=, /=) ops obviously do not (cannot) promote. +// ! and comparison are not mathematical ops. + +// Unary ops (+/-/~) change to unsigned upon width promotion, not otherwise. +// + is a unary op and produces promotion. +// std::common_type does not support unary operators. +// std::common_type::type == decltype(left - right) +// std::common_type::type != decltype(-value) +using integer = decltype(-'a'); +using character = std::common_type_t; +using common_character = std::common_type_t; +static_assert(!is_same_type); +static_assert(!is_same_type); + +// Compiler warns on bool safety for -b, ~b, <>b, /b, %b, b/, b% (assertions disabled). + +// invert +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); + +// comparison +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type= uint16_t{0}), bool>, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); +static_assert(is_same_type, "boolean"); + +// positive +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "unpromoted (>=32"); +static_assert(is_same_type, "unpromoted (>=32"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "unpromoted (>=32"); +static_assert(is_same_type, "unpromoted (>=32"); + +// negate (signed) +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "unpromoted (>=32"); +static_assert(is_same_type, "unpromoted (>=32"); +////static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +////static_assert(is_same_type, "unpromoted (>=32"); +////static_assert(is_same_type, "unpromoted (>=32"); + +// negate (unsigned) +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "unpromoted (>=32"); +static_assert(is_same_type, "unpromoted (>=32"); +////static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +////static_assert(is_same_type, "unpromoted (>=32"); +////static_assert(is_same_type, "unpromoted (>=32"); + +// ones_complement +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "unpromoted (>=32"); +static_assert(is_same_type, "unpromoted (>=32"); +////static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "unpromoted (>=32"); +static_assert(is_same_type, "unpromoted (>=32"); + +// shift_left +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "unpromoted (>=32"); +static_assert(is_same_type, "unpromoted (>=32"); +////static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "promoted (<32)"); +static_assert(is_same_type, "unpromoted (>=32"); +static_assert(is_same_type, "unpromoted (>=32"); + +// shift_right +static_assert(is_same_type> char{0}), int>, "promoted (<32)"); +static_assert(is_same_type> uint8_t{0}), int>, "promoted (<32)"); +static_assert(is_same_type> uint16_t{0}), int>, "promoted (<32)"); +static_assert(is_same_type> uint32_t{0}), int >, "promoted (<32)"); +static_assert(is_same_type> uint64_t{0}), uint32_t>, "unpromoted (>=32"); +static_assert(is_same_type> uint64_t{0}), uint64_t>, "unpromoted (>=32"); +////static_assert(is_same_type> bool{false}), int>, "promoted (<32)"); +static_assert(is_same_type> uint32_t{0}), int>, "promoted (<32)"); +static_assert(is_same_type> int8_t{0}), int>, "promoted (<32)"); +static_assert(is_same_type> int16_t{0}), int>, "promoted (<32)"); +static_assert(is_same_type> int32_t{0}), int>, "promoted (<32)"); +static_assert(is_same_type> int64_t{0}), uint32_t>, "unpromoted (>=32"); +static_assert(is_same_type> uint32_t{0}), int64_t>, "unpromoted (>=32"); + +// bit_and +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "unsigned (two >=32, s/u)"); +static_assert(is_same_type, "unsigned (two >=32, u/s)"); + +// bit_or +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "unsigned (two >=32, s/u)"); +static_assert(is_same_type, "unsigned (two >=32, u/s)"); + +// bit_xor +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "unsigned (two >=32, s/u)"); +static_assert(is_same_type, "unsigned (two >=32, u/s)"); + +// add +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "unsigned (two >=32, s/u)"); +static_assert(is_same_type, "unsigned (two >=32, u/s)"); + +// subtract +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "unsigned (two >=32, s/u)"); +static_assert(is_same_type, "unsigned (two >=32, u/s)"); + +// subtract (reversed) +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "unsigned (two >=32, s-u)"); +static_assert(is_same_type, "unsigned (two >=32, u-s)"); + +// multiply +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "unsigned (two >=32, s/u)"); +static_assert(is_same_type, "unsigned (two >=32, u/s)"); + +// divide +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +////static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "unsigned (two >=32, s/u)"); +static_assert(is_same_type, "unsigned (two >=32, u/s)"); + +// divide (reversed) +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +////static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "unsigned (two >=32, s/u)"); +static_assert(is_same_type, "unsigned (two >=32, u/s)"); + +// modulo +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +////static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "unsigned (two >=32, s%u)"); +static_assert(is_same_type, "unsigned (two >=32, u%s)"); + +// modulo (reversed) +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +static_assert(is_same_type, "widest (two >=32)"); +////static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "promoted (two <32)"); +static_assert(is_same_type, "widest (one >=32)"); +static_assert(is_same_type, "unsigned (two >=32, s%u)"); +static_assert(is_same_type, "unsigned (two >=32, u%s)");