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/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;
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