diff --git a/fuzzing/old_crashes/fuzz_snprintf/crash-cbafdfac1d7401408ca260cef95ab6948eedc646 b/fuzzing/old_crashes/fuzz_snprintf/crash-cbafdfac1d7401408ca260cef95ab6948eedc646 new file mode 100644 index 000000000..3d07eb9f0 --- /dev/null +++ b/fuzzing/old_crashes/fuzz_snprintf/crash-cbafdfac1d7401408ca260cef95ab6948eedc646 @@ -0,0 +1 @@ +Dd00000000001000000000000000000000000000000000001000000000cccccccccÿCccc0ccccccccc8888000010000)001.2 \ No newline at end of file diff --git a/fuzzing/old_crashes/fuzz_snprintf/crash-da39a3ee5e6b4b0d3255bfef95601890afd80709 b/fuzzing/old_crashes/fuzz_snprintf/crash-da39a3ee5e6b4b0d3255bfef95601890afd80709 new file mode 100644 index 000000000..e69de29bb diff --git a/fuzzing/old_crashes/fuzz_snprintf/crash-github-1 b/fuzzing/old_crashes/fuzz_snprintf/crash-github-1 new file mode 100644 index 000000000..2c1b4a2a4 --- /dev/null +++ b/fuzzing/old_crashes/fuzz_snprintf/crash-github-1 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 diff --git a/include/boost/decimal/charconv.hpp b/include/boost/decimal/charconv.hpp index 3ec1c90d2..1bbadccd0 100644 --- a/include/boost/decimal/charconv.hpp +++ b/include/boost/decimal/charconv.hpp @@ -353,7 +353,7 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_scientific_impl(char* first, char* last, c } const auto fp = fpclassify(value); - if (fp != FP_NORMAL) + if (!(fp == FP_NORMAL || fp == FP_SUBNORMAL)) { return to_chars_nonfinite(first, last, value, fp, fmt, precision); } @@ -494,7 +494,7 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_fixed_impl(char* first, char* last, const } const auto fp = fpclassify(value); - if (fp != FP_NORMAL) + if (!(fp == FP_NORMAL || fp == FP_SUBNORMAL)) { return to_chars_nonfinite(first, last, value, fp, fmt, precision); } @@ -720,7 +720,7 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_hex_impl(char* first, char* last, const Ta } const auto fp = fpclassify(value); - if (fp != FP_NORMAL) + if (!(fp == FP_NORMAL || fp == FP_SUBNORMAL)) { return to_chars_nonfinite(first, last, value, fp, chars_format::hex, precision); } @@ -742,7 +742,7 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_hex_impl(char* first, char* last, const Ta Unsigned_Integer significand = frexp10(value, &exp); BOOST_DECIMAL_ASSERT(significand != 0); // Strip zeros of the significand since frexp10 normalizes it - while (significand % 10U == 0) + while (significand % 10U == 0 && significand > 0) { significand /= 10U; ++exp; diff --git a/include/boost/decimal/decimal128.hpp b/include/boost/decimal/decimal128.hpp index 9639eb409..ab1df71a9 100644 --- a/include/boost/decimal/decimal128.hpp +++ b/include/boost/decimal/decimal128.hpp @@ -2238,18 +2238,18 @@ struct numeric_limits static constexpr int digits10 = digits; static constexpr int max_digits10 = digits; static constexpr int radix = 10; - static constexpr int min_exponent = -6142; + static constexpr int min_exponent = -6143; static constexpr int min_exponent10 = min_exponent; - static constexpr int max_exponent = 6145; + static constexpr int max_exponent = 6144; static constexpr int max_exponent10 = max_exponent; static constexpr bool traps = numeric_limits::traps; static constexpr bool tinyness_before = true; // Member functions static constexpr auto (min) () -> boost::decimal::decimal128 { return {1, min_exponent}; } - static constexpr auto (max) () -> boost::decimal::decimal128 { return {boost::decimal::detail::uint128{UINT64_C(999'999'999'999'999), UINT64_C(9'999'999'999'999'999'999)}, max_exponent}; } - static constexpr auto lowest () -> boost::decimal::decimal128 { return {boost::decimal::detail::uint128{UINT64_C(999'999'999'999'999), UINT64_C(9'999'999'999'999'999'999)}, max_exponent, true}; } - static constexpr auto epsilon () -> boost::decimal::decimal128 { return {1, -34}; } + static constexpr auto (max) () -> boost::decimal::decimal128 { return {boost::decimal::detail::uint128{UINT64_C(0b1111011010000100110111110101011011000011111000000), UINT64_C(0b0011011110001101100011100110001111111111111111111111111111111111)}, max_exponent - digits + 1}; } + static constexpr auto lowest () -> boost::decimal::decimal128 { return {boost::decimal::detail::uint128{UINT64_C(0b1111011010000100110111110101011011000011111000000), UINT64_C(0b0011011110001101100011100110001111111111111111111111111111111111)}, max_exponent - digits + 1, true}; } + static constexpr auto epsilon () -> boost::decimal::decimal128 { return {1, -digits + 1}; } static constexpr auto round_error () -> boost::decimal::decimal128 { return epsilon(); } static constexpr auto infinity () -> boost::decimal::decimal128 { return boost::decimal::from_bits(boost::decimal::detail::d128_inf_mask); } static constexpr auto quiet_NaN () -> boost::decimal::decimal128 { return boost::decimal::from_bits(boost::decimal::detail::d128_nan_mask); } diff --git a/include/boost/decimal/decimal128_fast.hpp b/include/boost/decimal/decimal128_fast.hpp index 8dcfce5e0..57920d613 100644 --- a/include/boost/decimal/decimal128_fast.hpp +++ b/include/boost/decimal/decimal128_fast.hpp @@ -1484,25 +1484,25 @@ struct numeric_limits static constexpr int digits10 = digits; static constexpr int max_digits10 = digits; static constexpr int radix = 10; - static constexpr int min_exponent = -6142; + static constexpr int min_exponent = -6143; static constexpr int min_exponent10 = min_exponent; - static constexpr int max_exponent = 6145; + static constexpr int max_exponent = 6144; static constexpr int max_exponent10 = max_exponent; static constexpr bool traps = numeric_limits::traps; static constexpr bool tinyness_before = true; // Member functions static constexpr auto (min) () -> boost::decimal::decimal128_fast { return {1, min_exponent}; } - static constexpr auto (max) () -> boost::decimal::decimal128_fast { return {boost::decimal::detail::uint128{UINT64_C(999'999'999'999'999), UINT64_C(9'999'999'999'999'999'999)}, max_exponent}; } - static constexpr auto lowest () -> boost::decimal::decimal128_fast { return {boost::decimal::detail::uint128{UINT64_C(999'999'999'999'999), UINT64_C(9'999'999'999'999'999'999)}, max_exponent, true}; } - static constexpr auto epsilon () -> boost::decimal::decimal128_fast { return {1, -34}; } + static constexpr auto (max) () -> boost::decimal::decimal128_fast { return {boost::decimal::detail::uint128{UINT64_C(0b1111011010000100110111110101011011000011111000000), UINT64_C(0b0011011110001101100011100110001111111111111111111111111111111111)}, max_exponent - digits + 1}; } + static constexpr auto lowest () -> boost::decimal::decimal128_fast { return {boost::decimal::detail::uint128{UINT64_C(0b1111011010000100110111110101011011000011111000000), UINT64_C(0b0011011110001101100011100110001111111111111111111111111111111111)}, max_exponent - digits + 1, true}; } + static constexpr auto epsilon () -> boost::decimal::decimal128_fast { return {1, -digits + 1}; } static constexpr auto round_error () -> boost::decimal::decimal128_fast { return epsilon(); } static constexpr auto infinity () -> boost::decimal::decimal128_fast { return boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_inf, 0, false); } static constexpr auto quiet_NaN () -> boost::decimal::decimal128_fast { return boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_qnan, 0, false); } static constexpr auto signaling_NaN() -> boost::decimal::decimal128_fast { return boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_snan, 0, false); } - static constexpr auto denorm_min () -> boost::decimal::decimal128_fast { return {1, boost::decimal::detail::etiny_v}; } + static constexpr auto denorm_min () -> boost::decimal::decimal128_fast { return min(); } }; -} +} // namspace std #endif //BOOST_DECIMAL_DECIMAL128_FAST_HPP diff --git a/include/boost/decimal/decimal32.hpp b/include/boost/decimal/decimal32.hpp index e2454fbb4..55109730b 100644 --- a/include/boost/decimal/decimal32.hpp +++ b/include/boost/decimal/decimal32.hpp @@ -2248,9 +2248,9 @@ struct numeric_limits // Member functions static constexpr auto (min) () -> boost::decimal::decimal32 { return {1, min_exponent}; } - static constexpr auto (max) () -> boost::decimal::decimal32 { return {9'999'999, max_exponent}; } - static constexpr auto lowest () -> boost::decimal::decimal32 { return {-9'999'999, max_exponent}; } - static constexpr auto epsilon () -> boost::decimal::decimal32 { return {1, -7}; } + static constexpr auto (max) () -> boost::decimal::decimal32 { return {9'999'999, max_exponent - digits + 1}; } + static constexpr auto lowest () -> boost::decimal::decimal32 { return {9'999'999, max_exponent - digits + 1, true}; } + static constexpr auto epsilon () -> boost::decimal::decimal32 { return {1, -digits + 1}; } static constexpr auto round_error () -> boost::decimal::decimal32 { return epsilon(); } static constexpr auto infinity () -> boost::decimal::decimal32 { return boost::decimal::from_bits(boost::decimal::detail::d32_inf_mask); } static constexpr auto quiet_NaN () -> boost::decimal::decimal32 { return boost::decimal::from_bits(boost::decimal::detail::d32_nan_mask); } diff --git a/include/boost/decimal/decimal32_fast.hpp b/include/boost/decimal/decimal32_fast.hpp index 2de21e4eb..1841bf15e 100644 --- a/include/boost/decimal/decimal32_fast.hpp +++ b/include/boost/decimal/decimal32_fast.hpp @@ -1481,16 +1481,16 @@ struct numeric_limits // Member functions static constexpr auto (min) () -> boost::decimal::decimal32_fast { return {1, min_exponent}; } - static constexpr auto (max) () -> boost::decimal::decimal32_fast { return {9'999'999, max_exponent}; } - static constexpr auto lowest () -> boost::decimal::decimal32_fast { return {-9'999'999, max_exponent}; } - static constexpr auto epsilon () -> boost::decimal::decimal32_fast { return {1, -7}; } + static constexpr auto (max) () -> boost::decimal::decimal32_fast { return {9'999'999, max_exponent - digits + 1}; } + static constexpr auto lowest () -> boost::decimal::decimal32_fast { return {9'999'999, max_exponent - digits + 1, true}; } + static constexpr auto epsilon () -> boost::decimal::decimal32_fast { return {1, -digits + 1}; } static constexpr auto round_error () -> boost::decimal::decimal32_fast { return epsilon(); } static constexpr auto infinity () -> boost::decimal::decimal32_fast { return boost::decimal::direct_init(boost::decimal::detail::d32_fast_inf, UINT8_C((0))); } static constexpr auto quiet_NaN () -> boost::decimal::decimal32_fast { return boost::decimal::direct_init(boost::decimal::detail::d32_fast_qnan, UINT8_C((0))); } static constexpr auto signaling_NaN() -> boost::decimal::decimal32_fast { return boost::decimal::direct_init(boost::decimal::detail::d32_fast_snan, UINT8_C((0))); } // With denorm absent returns the same value as min - static constexpr auto denorm_min () -> boost::decimal::decimal32_fast { return {1, min_exponent}; } + static constexpr auto denorm_min () -> boost::decimal::decimal32_fast { return min(); } }; } // Namespace std diff --git a/include/boost/decimal/decimal64.hpp b/include/boost/decimal/decimal64.hpp index 52a8af3bf..3440fb33a 100644 --- a/include/boost/decimal/decimal64.hpp +++ b/include/boost/decimal/decimal64.hpp @@ -2149,18 +2149,18 @@ struct numeric_limits static constexpr int digits10 = digits; static constexpr int max_digits10 = digits; static constexpr int radix = 10; - static constexpr int min_exponent = -382; + static constexpr int min_exponent = -383; static constexpr int min_exponent10 = min_exponent; - static constexpr int max_exponent = 385; + static constexpr int max_exponent = 384; static constexpr int max_exponent10 = max_exponent; static constexpr bool traps = numeric_limits::traps; static constexpr bool tinyness_before = true; // Member functions static constexpr auto (min) () -> boost::decimal::decimal64 { return {1, min_exponent}; } - static constexpr auto (max) () -> boost::decimal::decimal64 { return {9'999'999'999'999'999, max_exponent}; } - static constexpr auto lowest () -> boost::decimal::decimal64 { return {-9'999'999'999'999'999, max_exponent}; } - static constexpr auto epsilon () -> boost::decimal::decimal64 { return {1, -16}; } + static constexpr auto (max) () -> boost::decimal::decimal64 { return {9'999'999'999'999'999, max_exponent - digits + 1}; } + static constexpr auto lowest () -> boost::decimal::decimal64 { return {9'999'999'999'999'999, max_exponent - digits + 1, true}; } + static constexpr auto epsilon () -> boost::decimal::decimal64 { return {1, -digits + 1}; } static constexpr auto round_error () -> boost::decimal::decimal64 { return epsilon(); } static constexpr auto infinity () -> boost::decimal::decimal64 { return boost::decimal::from_bits(boost::decimal::detail::d64_inf_mask); } static constexpr auto quiet_NaN () -> boost::decimal::decimal64 { return boost::decimal::from_bits(boost::decimal::detail::d64_nan_mask); } diff --git a/include/boost/decimal/decimal64_fast.hpp b/include/boost/decimal/decimal64_fast.hpp index 2946711da..26cc32bbf 100644 --- a/include/boost/decimal/decimal64_fast.hpp +++ b/include/boost/decimal/decimal64_fast.hpp @@ -1427,18 +1427,18 @@ struct numeric_limits static constexpr int digits10 = digits; static constexpr int max_digits10 = digits; static constexpr int radix = 10; - static constexpr int min_exponent = -382; + static constexpr int min_exponent = -383; static constexpr int min_exponent10 = min_exponent; - static constexpr int max_exponent = 385; + static constexpr int max_exponent = 384; static constexpr int max_exponent10 = max_exponent; static constexpr bool traps = numeric_limits::traps; static constexpr bool tinyness_before = true; // Member functions static constexpr auto (min) () -> boost::decimal::decimal64_fast { return {1, min_exponent}; } - static constexpr auto (max) () -> boost::decimal::decimal64_fast { return {9'999'999'999'999'999, max_exponent}; } - static constexpr auto lowest () -> boost::decimal::decimal64_fast { return {-9'999'999'999'999'999, max_exponent}; } - static constexpr auto epsilon () -> boost::decimal::decimal64_fast { return {1, -16}; } + static constexpr auto (max) () -> boost::decimal::decimal64_fast { return {9'999'999'999'999'999, max_exponent - digits + 1}; } + static constexpr auto lowest () -> boost::decimal::decimal64_fast { return {9'999'999'999'999'999, max_exponent - digits + 1, true}; } + static constexpr auto epsilon () -> boost::decimal::decimal64_fast { return {1, -digits + 1}; } static constexpr auto round_error () -> boost::decimal::decimal64_fast { return epsilon(); } static constexpr auto infinity () -> boost::decimal::decimal64_fast { return boost::decimal::direct_init_d64( boost::decimal::detail::d64_fast_inf, 0, false); } @@ -1446,7 +1446,7 @@ struct numeric_limits boost::decimal::detail::d64_fast_qnan, 0, false); } static constexpr auto signaling_NaN() -> boost::decimal::decimal64_fast { return boost::decimal::direct_init_d64( boost::decimal::detail::d64_fast_snan, 0, false); } - static constexpr auto denorm_min () -> boost::decimal::decimal64_fast { return {1, boost::decimal::detail::etiny_v}; } + static constexpr auto denorm_min () -> boost::decimal::decimal64_fast { return min(); } }; } // namespace std diff --git a/include/boost/decimal/detail/comparison.hpp b/include/boost/decimal/detail/comparison.hpp index f968185a0..a23b6aebc 100644 --- a/include/boost/decimal/detail/comparison.hpp +++ b/include/boost/decimal/detail/comparison.hpp @@ -57,15 +57,36 @@ BOOST_DECIMAL_FORCE_INLINE constexpr auto equality_impl(DecimalType lhs, Decimal const auto delta_exp {lhs_exp - rhs_exp}; - if (delta_exp > detail::precision_v || delta_exp < -detail::precision_v || - ((lhs_sig == static_cast(0)) ^ (rhs_sig == static_cast(0)))) + if (lhs_sig == 0 && rhs_sig == 0) + { + // The difference in exponent is irrelevant here + return true; + } + if (delta_exp > detail::precision_v || delta_exp < -detail::precision_v) { return false; } // Step 5: Normalize the significand and compare - delta_exp >= 0 ? lhs_sig *= detail::pow10(static_cast(delta_exp)) : - rhs_sig *= detail::pow10(static_cast(-delta_exp)); + // Instead of multiplying the larger number, divide the smaller one + if (delta_exp >= 0) + { + // Check if we can divide rhs_sig safely + if (delta_exp > 0 && rhs_sig % detail::pow10(static_cast(delta_exp)) != 0) + { + return false; + } + rhs_sig /= detail::pow10(static_cast(delta_exp)); + } + else + { + // Check if we can divide lhs_sig safely + if (lhs_sig % detail::pow10(static_cast(-delta_exp)) != 0) + { + return false; + } + lhs_sig /= detail::pow10(static_cast(-delta_exp)); + } return lhs_sig == rhs_sig; } @@ -93,19 +114,34 @@ constexpr auto equal_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign, // Check the value of delta exp to avoid to large a value for pow10 // Also if only one of the significands is 0 then we know the values have to be mismatched - if (delta_exp > detail::precision_v || delta_exp < -detail::precision_v || - ((new_lhs_sig == static_cast(0)) ^ (new_rhs_sig == static_cast(0)))) + if (new_lhs_sig == static_cast(0) && new_rhs_sig == static_cast(0)) + { + return true; + } + if (delta_exp > detail::precision_v || delta_exp < -detail::precision_v) { return false; } + // Step 5: Normalize the significand and compare + // Instead of multiplying the larger number, divide the smaller one if (delta_exp >= 0) { - new_lhs_sig *= detail::pow10(static_cast(delta_exp)); + // Check if we can divide rhs_sig safely + if (delta_exp > 0 && new_rhs_sig % detail::pow10(static_cast(delta_exp)) != 0) + { + return false; + } + new_rhs_sig /= detail::pow10(static_cast(delta_exp)); } else { - new_rhs_sig *= detail::pow10(static_cast(-delta_exp)); + // Check if we can divide lhs_sig safely + if (new_lhs_sig % detail::pow10(static_cast(-delta_exp)) != 0) + { + return false; + } + new_lhs_sig /= detail::pow10(static_cast(-delta_exp)); } #ifdef BOOST_DECIMAL_DEBUG_EQUAL diff --git a/include/boost/decimal/detail/config.hpp b/include/boost/decimal/detail/config.hpp index fe4f36dc9..d5a713cb1 100644 --- a/include/boost/decimal/detail/config.hpp +++ b/include/boost/decimal/detail/config.hpp @@ -244,7 +244,7 @@ typedef unsigned __int128 uint128_t; #endif // https://github.com/llvm/llvm-project/issues/55638 -#if defined(__clang__) && __cplusplus > 202002L +#if defined(__clang__) && __cplusplus > 202002L && __clang_major__ < 17 # undef BOOST_DECIMAL_IS_CONSTANT_EVALUATED # define BOOST_DECIMAL_IS_CONSTANT_EVALUATED(x) false # define BOOST_DECIMAL_NO_CONSTEVAL_DETECTION diff --git a/test/Jamfile b/test/Jamfile index 763173f66..ac6bc9f10 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -124,6 +124,7 @@ run test_implicit_integral_conversion.cpp ; run test_laguerre.cpp ; run test_legendre.cpp ; run test_literals.cpp ; +run test_limits.cpp ; run test_lgamma.cpp ; run test_log.cpp ; run test_log1p.cpp ; diff --git a/test/test_decimal32.cpp b/test/test_decimal32.cpp index b62ef135d..723cfe0ba 100644 --- a/test/test_decimal32.cpp +++ b/test/test_decimal32.cpp @@ -261,15 +261,6 @@ void test_addition() BOOST_TEST(isinf(one + inf_val)); BOOST_TEST(isnan(inf_val + qnan_val)); BOOST_TEST(isnan(qnan_val + inf_val)); - - // Overflow - constexpr decimal32 max_val((std::numeric_limits::max)()); - if (!BOOST_TEST(isinf(max_val + one))) - { - // LCOV_EXCL_START - std::cerr << std::bitset<32>(to_bits(max_val + one)) << std::endl; - // LCOV_EXCL_STOP - } } void test_subtraction() @@ -320,20 +311,6 @@ void test_subtraction() BOOST_TEST(isinf(one - inf_val)); BOOST_TEST(isnan(inf_val - qnan_val)); BOOST_TEST(isnan(qnan_val - inf_val)); - - // Why does MSVC 14.1 warn about unary minus but nothing else does? - #ifdef _MSC_VER - # pragma warning(push) - # pragma warning(disable: 4146) - #endif - - // Underflow - constexpr decimal32 lowest_val(std::numeric_limits::lowest()); - BOOST_TEST(isinf(lowest_val - one)); - - #ifdef _MSC_VER - # pragma warning(pop) - #endif } void test_multiplicatiom() diff --git a/test/test_fixed_width_trunc.cpp b/test/test_fixed_width_trunc.cpp index 482fa568e..f6763653d 100644 --- a/test/test_fixed_width_trunc.cpp +++ b/test/test_fixed_width_trunc.cpp @@ -11,14 +11,12 @@ using namespace boost::decimal; template void test() { - constexpr T test_val {UINT32_C(1234567), 0}; - constexpr T validation_val_1 {UINT32_C(1), 6}; - constexpr T validation_val_2 {UINT32_C(12), 5}; - constexpr T validation_val_3 {UINT32_C(123), 4}; - constexpr T validation_val_4 {UINT32_C(1235), 3}; - constexpr T validation_val_5 {UINT32_C(12346), 2}; - constexpr T validation_val_6 {UINT32_C(123457), 1}; - constexpr T validation_val_7 {test_val}; + constexpr T test_val {UINT32_C(123456), 0}; + constexpr T validation_val_1 {UINT32_C(1), 5}; + constexpr T validation_val_2 {UINT32_C(12), 4}; + constexpr T validation_val_3 {UINT32_C(123), 3}; + constexpr T validation_val_4 {UINT32_C(1235), 2}; + constexpr T validation_val_5 {UINT32_C(12346), 1}; BOOST_TEST_EQ(rescale(test_val, 0), rescale(test_val)); BOOST_TEST_EQ(rescale(test_val, 1), validation_val_1); @@ -26,8 +24,6 @@ void test() BOOST_TEST_EQ(rescale(test_val, 3), validation_val_3); BOOST_TEST_EQ(rescale(test_val, 4), validation_val_4); BOOST_TEST_EQ(rescale(test_val, 5), validation_val_5); - BOOST_TEST_EQ(rescale(test_val, 6), validation_val_6); - BOOST_TEST_EQ(rescale(test_val, 7), validation_val_7); BOOST_TEST_EQ(rescale(test_val, 100), test_val); // Non-finite values diff --git a/test/test_limits.cpp b/test/test_limits.cpp new file mode 100644 index 000000000..12fc39e0e --- /dev/null +++ b/test/test_limits.cpp @@ -0,0 +1,130 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include + +#if defined(__cpp_consteval) && __cpp_consteval >= 201811L && !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) + +#include + +using namespace boost::decimal; + +template +consteval bool test_value(T value, std::string_view res) +{ + char buffer[256]; + const auto r = to_chars(buffer, buffer + sizeof(buffer), value, chars_format::scientific, 40); + *r.ptr = '\0'; + std::string_view buffer_view {buffer}; + return res == buffer_view; +} + +int main() +{ + // Max + static_assert(test_value(std::numeric_limits::max(), "9.9999990000000000000000000000000000000000e+96")); + static_assert(test_value(std::numeric_limits::max(), "9.9999990000000000000000000000000000000000e+96")); + static_assert(test_value(std::numeric_limits::max(), "9.9999999999999990000000000000000000000000e+384")); + static_assert(test_value(std::numeric_limits::max(), "9.9999999999999990000000000000000000000000e+384")); + static_assert(test_value(std::numeric_limits::max(), "9.9999999999999999999999999999999990000000e+6144")); + static_assert(test_value(std::numeric_limits::max(), "9.9999999999999999999999999999999990000000e+6144")); + + // Epsilon + static_assert(test_value(std::numeric_limits::epsilon(), "1.0000000000000000000000000000000000000000e-06")); + static_assert(test_value(std::numeric_limits::epsilon(), "1.0000000000000000000000000000000000000000e-06")); + static_assert(test_value(std::numeric_limits::epsilon(), "1.0000000000000000000000000000000000000000e-15")); + static_assert(test_value(std::numeric_limits::epsilon(), "1.0000000000000000000000000000000000000000e-15")); + static_assert(test_value(std::numeric_limits::epsilon(), "1.0000000000000000000000000000000000000000e-33")); + static_assert(test_value(std::numeric_limits::epsilon(), "1.0000000000000000000000000000000000000000e-33")); + + // Min + static_assert(test_value(std::numeric_limits::min(), "1.0000000000000000000000000000000000000000e-95")); + static_assert(test_value(std::numeric_limits::min(), "1.0000000000000000000000000000000000000000e-95")); + static_assert(test_value(std::numeric_limits::min(), "1.0000000000000000000000000000000000000000e-383")); + static_assert(test_value(std::numeric_limits::min(), "1.0000000000000000000000000000000000000000e-383")); + static_assert(test_value(std::numeric_limits::min(), "1.0000000000000000000000000000000000000000e-6143")); + static_assert(test_value(std::numeric_limits::min(), "1.0000000000000000000000000000000000000000e-6143")); + + // Min subnormal + // Fast types don't support sub-normals so they should return min + static_assert(test_value(std::numeric_limits::denorm_min(), "1.0000000000000000000000000000000000000000e-101")); + static_assert(test_value(std::numeric_limits::denorm_min(), "1.0000000000000000000000000000000000000000e-95")); + static_assert(test_value(std::numeric_limits::denorm_min(), "1.0000000000000000000000000000000000000000e-398")); + static_assert(test_value(std::numeric_limits::denorm_min(), "1.0000000000000000000000000000000000000000e-383")); + static_assert(test_value(std::numeric_limits::denorm_min(), "1.0000000000000000000000000000000000000000e-6176")); + static_assert(test_value(std::numeric_limits::denorm_min(), "1.0000000000000000000000000000000000000000e-6143")); + + // Lowest + max should be 0 + static_assert(test_value(std::numeric_limits::max() + std::numeric_limits::lowest(), "0.0000000000000000000000000000000000000000e+00")); + static_assert(test_value(std::numeric_limits::max() + std::numeric_limits::lowest(), "0.0000000000000000000000000000000000000000e+00")); + static_assert(test_value(std::numeric_limits::max() + std::numeric_limits::lowest(), "0.0000000000000000000000000000000000000000e+00")); + static_assert(test_value(std::numeric_limits::max() + std::numeric_limits::lowest(), "0.0000000000000000000000000000000000000000e+00")); + static_assert(test_value(std::numeric_limits::max() + std::numeric_limits::lowest(), "0.0000000000000000000000000000000000000000e+00")); + static_assert(test_value(std::numeric_limits::max() + std::numeric_limits::lowest(), "0.0000000000000000000000000000000000000000e+00")); + + return boost::report_errors(); +} + +#else + +using namespace boost::decimal; + +template +void test_value(T value, const char* res) +{ + char buffer[256]; + const auto r = to_chars(buffer, buffer + sizeof(buffer), value, chars_format::scientific, 40); + *r.ptr = '\0'; + BOOST_TEST(r); + BOOST_TEST_CSTR_EQ(buffer, res); +} + +int main() +{ + // Max + test_value(std::numeric_limits::max(), "9.9999990000000000000000000000000000000000e+96"); + test_value(std::numeric_limits::max(), "9.9999990000000000000000000000000000000000e+96"); + test_value(std::numeric_limits::max(), "9.9999999999999990000000000000000000000000e+384"); + test_value(std::numeric_limits::max(), "9.9999999999999990000000000000000000000000e+384"); + test_value(std::numeric_limits::max(), "9.9999999999999999999999999999999990000000e+6144"); + test_value(std::numeric_limits::max(), "9.9999999999999999999999999999999990000000e+6144"); + + // Epsilon + test_value(std::numeric_limits::epsilon(), "1.0000000000000000000000000000000000000000e-06"); + test_value(std::numeric_limits::epsilon(), "1.0000000000000000000000000000000000000000e-06"); + test_value(std::numeric_limits::epsilon(), "1.0000000000000000000000000000000000000000e-15"); + test_value(std::numeric_limits::epsilon(), "1.0000000000000000000000000000000000000000e-15"); + test_value(std::numeric_limits::epsilon(), "1.0000000000000000000000000000000000000000e-33"); + test_value(std::numeric_limits::epsilon(), "1.0000000000000000000000000000000000000000e-33"); + + // Min + test_value(std::numeric_limits::min(), "1.0000000000000000000000000000000000000000e-95"); + test_value(std::numeric_limits::min(), "1.0000000000000000000000000000000000000000e-95"); + test_value(std::numeric_limits::min(), "1.0000000000000000000000000000000000000000e-383"); + test_value(std::numeric_limits::min(), "1.0000000000000000000000000000000000000000e-383"); + test_value(std::numeric_limits::min(), "1.0000000000000000000000000000000000000000e-6143"); + test_value(std::numeric_limits::min(), "1.0000000000000000000000000000000000000000e-6143"); + + // Min subnormal + // Fast types don't support sub-normals so they should return min + test_value(std::numeric_limits::denorm_min(), "1.0000000000000000000000000000000000000000e-101"); + test_value(std::numeric_limits::denorm_min(), "1.0000000000000000000000000000000000000000e-95"); + test_value(std::numeric_limits::denorm_min(), "1.0000000000000000000000000000000000000000e-398"); + test_value(std::numeric_limits::denorm_min(), "1.0000000000000000000000000000000000000000e-383"); + test_value(std::numeric_limits::denorm_min(), "1.0000000000000000000000000000000000000000e-6176"); + test_value(std::numeric_limits::denorm_min(), "1.0000000000000000000000000000000000000000e-6143"); + + // Lowest + max + test_value(std::numeric_limits::max() + std::numeric_limits::lowest(), "0.0000000000000000000000000000000000000000e+00"); + test_value(std::numeric_limits::max() + std::numeric_limits::lowest(), "0.0000000000000000000000000000000000000000e+00"); + test_value(std::numeric_limits::max() + std::numeric_limits::lowest(), "0.0000000000000000000000000000000000000000e+00"); + test_value(std::numeric_limits::max() + std::numeric_limits::lowest(), "0.0000000000000000000000000000000000000000e+00"); + test_value(std::numeric_limits::max() + std::numeric_limits::lowest(), "0.0000000000000000000000000000000000000000e+00"); + test_value(std::numeric_limits::max() + std::numeric_limits::lowest(), "0.0000000000000000000000000000000000000000e+00"); + + return boost::report_errors(); +} + +#endif diff --git a/test/test_snprintf.cpp b/test/test_snprintf.cpp index 82867e6b0..0ac2ee457 100644 --- a/test/test_snprintf.cpp +++ b/test/test_snprintf.cpp @@ -170,6 +170,37 @@ void test_bootstrap() } } +void test_fuzzer_crash(const char* c_data) +{ + auto size = std::strlen(c_data); + + const auto formats = std::array{boost::decimal::chars_format::general, + boost::decimal::chars_format::fixed, + boost::decimal::chars_format::scientific, + boost::decimal::chars_format::hex}; + + const auto dec32_printf_formats = std::array{"%Hg", "%Hf", "%He", "%Ha"}; + const auto dec64_printf_formats = std::array{"%Dg", "%Df", "%De", "%Da"}; + const auto dec128_printf_formats = std::array{"%DDg", "%DDf", "%DDe", "%DDa"}; + + for (std::size_t i {}; i < 4; ++i) + { + char buffer[20]; // Small enough it should overflow sometimes + + boost::decimal::decimal32 f_val {}; + boost::decimal::from_chars(c_data, c_data + size, f_val, formats[i]); + boost::decimal::snprintf(buffer, sizeof(buffer), dec32_printf_formats[i], f_val); + + boost::decimal::decimal64 val {}; + boost::decimal::from_chars(c_data, c_data + size, val, formats[i]); + boost::decimal::snprintf(buffer, sizeof(buffer), dec64_printf_formats[i], val); + + boost::decimal::decimal128 ld_val {}; + boost::decimal::from_chars(c_data, c_data + size, ld_val, formats[i]); + boost::decimal::snprintf(buffer, sizeof(buffer), dec128_printf_formats[i], ld_val); + } +} + int main() { test_bootstrap(); @@ -181,5 +212,8 @@ int main() test_locales(); #endif + test_fuzzer_crash(""); + test_fuzzer_crash("Dd00000000001000000000000000000000000000000000001000000000ccccccccc�Cccc0ccccccccc8888000010000)001.2"); + return boost::report_errors(); } diff --git a/test/test_to_chars.cpp b/test/test_to_chars.cpp index ac60bf8b2..7a836f5d3 100644 --- a/test/test_to_chars.cpp +++ b/test/test_to_chars.cpp @@ -58,8 +58,6 @@ void test_error_value(const char* input, chars_format format, int precision = -1 T val; const auto r_from = from_chars(input, input + std::strlen(input), val, format); BOOST_TEST(r_from); - std::cerr << "Val: " << val - << "\nIsneg: " << signbit(val) << std::endl; char buffer[boost::decimal::limits::max_chars] {}; const auto r_to = to_chars(buffer, buffer + sizeof(buffer), val, format, precision); BOOST_TEST(r_to); @@ -873,9 +871,6 @@ int main() test_value(decimal32{504.29034} / decimal32{-727.45465}, "-0.693226", chars_format::general, 6); test_value(decimal32{504.29034} / decimal32{-727.45465}, "-6.932257e-01", chars_format::scientific, 6); - // See: https://github.com/cppalliance/decimal/issues/478 - test_value(std::numeric_limits::epsilon(), "1e-07"); - // Value found from fuzzing for (int precision = -1; precision < 10; ++precision) {