Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions include/boost/decimal/detail/cmath/cbrt.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2023 - 2024 Matt Borland
// Copyright 2023 - 2024 Christopher Kormanyos
// Copyright 2023 - 2025 Matt Borland
// Copyright 2023 - 2025 Christopher Kormanyos
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

Expand Down Expand Up @@ -55,20 +55,16 @@ constexpr auto cbrt_impl(const T x) noexcept

const auto gn { frexp10(x, &exp10val) };

const auto
zeros_removal
{
remove_trailing_zeros(gn)
};
int removed_zeros { };

const bool is_pure { static_cast<unsigned>(zeros_removal.trimmed_number) == 1U };
const bool is_pure { detail::patch::is_pure_p10<T>(gn, &removed_zeros) };

if(is_pure)
{
// Here, a pure power-of-10 argument gets a straightforward result.
// For argument 10^n where n is a multiple of 3, the result is exact.

const int p10 { exp10val + static_cast<int>(zeros_removal.number_of_removed_zeros) };
const int p10 { exp10val + removed_zeros };

if (p10 == 0)
{
Expand Down Expand Up @@ -131,14 +127,14 @@ constexpr auto cbrt_impl(const T x) noexcept
(five + gx * (seventy + gx * 56))
/ (numbers::cbrt2_v<T> * (fourteen + gx * (seventy + gx * 20)));

// Perform 2, 3 or 4 Newton-Raphson iterations depending on precision.
// Perform 3, 4 or 5 Newton-Raphson iterations depending on precision.
// Note from above, we start with slightly more than 2 decimal digits
// of accuracy.

constexpr int iter_loops
{
std::numeric_limits<T>::digits10 < 10 ? 2
: std::numeric_limits<T>::digits10 < 20 ? 3 : 4
std::numeric_limits<T>::digits10 < 10 ? 3
: std::numeric_limits<T>::digits10 < 20 ? 4 : 5
};

for (int idx = 0; idx < iter_loops; ++idx)
Expand Down
14 changes: 5 additions & 9 deletions include/boost/decimal/detail/cmath/log10.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2023 Matt Borland
// Copyright 2023 Christopher Kormanyos
// Copyright 2023 - 2025 Matt Borland
// Copyright 2023 - 2025 Christopher Kormanyos
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

Expand Down Expand Up @@ -57,18 +57,14 @@ constexpr auto log10_impl(const T x) noexcept

const auto gn { frexp10(x, &exp10val) };

const auto
zeros_removal
{
remove_trailing_zeros(gn)
};
int removed_zeros { };

const bool is_pure { static_cast<unsigned>(zeros_removal.trimmed_number) == 1U };
const bool is_pure { detail::patch::is_pure_p10<T>(gn, &removed_zeros) };

if(is_pure)
{
// Here, a pure power-of-10 argument gets a pure integral result.
const int p10 { exp10val + static_cast<int>(zeros_removal.number_of_removed_zeros) };
const int p10 { exp10val + removed_zeros };

result = T { p10 };
}
Expand Down
14 changes: 5 additions & 9 deletions include/boost/decimal/detail/cmath/pow.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2023 Matt Borland
// Copyright 2023 Christopher Kormanyos
// Copyright 2023 - 2025 Matt Borland
// Copyright 2023 - 2025 Christopher Kormanyos
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

Expand Down Expand Up @@ -101,18 +101,14 @@ constexpr auto pow(const T b, const IntegralType p) noexcept

const auto bn { frexp10(b, &exp10val) };

const auto
zeros_removal
{
detail::remove_trailing_zeros(bn)
};
int removed_zeros { };

const bool is_pure { static_cast<int>(zeros_removal.trimmed_number) == 1 };
const bool is_pure { detail::patch::is_pure_p10<T>(bn, &removed_zeros) };

if(is_pure)
{
// Here, a pure power-of-10 argument (b) gets a pure integral result.
const int log10_val { exp10val + static_cast<int>(zeros_removal.number_of_removed_zeros) };
const int log10_val { exp10val + removed_zeros };

result = T { 1, static_cast<int>(log10_val * static_cast<int>(p)) };
}
Expand Down
16 changes: 6 additions & 10 deletions include/boost/decimal/detail/cmath/sqrt.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2023 - 2024 Matt Borland
// Copyright 2023 - 2024 Christopher Kormanyos
// Copyright 2023 - 2025 Matt Borland
// Copyright 2023 - 2025 Christopher Kormanyos
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

Expand Down Expand Up @@ -55,13 +55,9 @@ constexpr auto sqrt_impl(const T x) noexcept

const auto gn { frexp10(x, &exp10val) };

const auto
zeros_removal
{
remove_trailing_zeros(gn)
};
int removed_zeros { };

const bool is_pure { static_cast<unsigned>(zeros_removal.trimmed_number) == 1U };
const bool is_pure { detail::patch::is_pure_p10<T>(gn, &removed_zeros) };

constexpr T one { 1 };

Expand All @@ -70,7 +66,7 @@ constexpr auto sqrt_impl(const T x) noexcept
// Here, a pure power-of-10 argument gets a straightforward result.
// For argument 10^n where n is even, the result is exact.

const int p10 { exp10val + static_cast<int>(zeros_removal.number_of_removed_zeros) };
const int p10 { exp10val + removed_zeros };

if (p10 == 0)
{
Expand Down Expand Up @@ -123,7 +119,7 @@ constexpr auto sqrt_impl(const T x) noexcept
(one + gx * ((one + gx) * 20))
/ (numbers::sqrt2_v<T> * ((gx * 4) * (five + gx) + five));

// Perform 2, 3 or 4 Newton-Raphson iterations depending on precision.
// Perform 3, 4 or 5 Newton-Raphson iterations depending on precision.
// Note from above, we start with slightly more than 2 decimal digits
// of accuracy.

Expand Down
51 changes: 51 additions & 0 deletions include/boost/decimal/detail/remove_trailing_zeros.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,57 @@ constexpr auto remove_trailing_zeros(boost::int128::detail::builtin_u128 n) noex

#endif

namespace patch {

#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // Unreachable code
#endif

template <typename T>
constexpr auto is_pure_p10(const typename T::significand_type& gn, int* p_removed_zeros) noexcept -> bool
{
const auto
zeros_removal
{
remove_trailing_zeros(gn)
};

bool is_pure { static_cast<int>(zeros_removal.trimmed_number) == 1 };

BOOST_DECIMAL_IF_CONSTEXPR(std::numeric_limits<typename T::significand_type>::digits > 64)
{
// Catch a rare overflow case for 128-bit decimal types when counting trailing zeros and
// patch it here. See also Git issue: https://github.com/cppalliance/decimal/issues/1110

if(is_pure)
{
// Scale the argument to the interval 1 <= x < 10.
// Scaling is needed to check floor-argument equality.
const T gx { gn, -std::numeric_limits<T>::digits10 + 1 };

if(floor(gx) != gx)
{
is_pure = false;
}
}
}

if(p_removed_zeros != nullptr)
{
*p_removed_zeros = static_cast<int>(zeros_removal.number_of_removed_zeros);
}

return is_pure;
}

#ifdef _MSC_VER
# pragma warning(pop)
#endif

} // namespace patch


} // namespace detail
} // namespace decimal
} // namespace boost
Expand Down
1 change: 1 addition & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ run github_issue_1057.cpp ;
compile-fail github_issue_1087.cpp ;
run github_issue_1091.cpp ;
run github_issue_1094.cpp ;
run github_issue_1110.cpp ;
run link_1.cpp link_2.cpp link_3.cpp ;
run quick.cpp ;
run random_decimal32_comp.cpp ;
Expand Down
104 changes: 104 additions & 0 deletions test/github_issue_1110.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2025 Christopher Kormanyos
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// See: https://github.com/cppalliance/decimal/issues/1110

#include <boost/decimal/decimal128_t.hpp>
#include <boost/decimal/cmath.hpp>
#include <boost/decimal/iostream.hpp>

#include <boost/core/lightweight_test.hpp>

#include <iomanip>
#include <iostream>
#include <sstream>

namespace local {

auto test_near1() -> void;
auto test_large() -> void;

auto test_near1() -> void
{
const boost::decimal::decimal128_t one { 1 };
const boost::decimal::decimal128_t del { 1, -32 };
const boost::decimal::decimal128_t sum { one + del };

const boost::decimal::decimal128_t sqr { sqrt(sum) };

{
std::stringstream strm { };

strm << std::setprecision(std::numeric_limits<boost::decimal::decimal128_t>::digits10) << sqr;

BOOST_TEST(strm.str() == "1.000000000000000000000000000000005");
}

const boost::decimal::decimal128_t cbr { cbrt(sum) };

{
std::stringstream strm { };

strm << std::setprecision(std::numeric_limits<boost::decimal::decimal128_t>::digits10)<< cbr;

BOOST_TEST(strm.str() == "1.000000000000000000000000000000003");
}

const boost::decimal::decimal128_t lgt { log10(sum) };

{
std::stringstream strm { };

strm << std::setprecision(std::numeric_limits<boost::decimal::decimal128_t>::digits10)<< lgt;

BOOST_TEST(strm.str() == "4.4e-33");
}
}

auto test_large() -> void
{
const boost::decimal::decimal128_t one { 1, 100 };
const boost::decimal::decimal128_t del { 1, 68 };
const boost::decimal::decimal128_t sum { one + del };

const boost::decimal::decimal128_t sqr { sqrt(sum) };

{
std::stringstream strm { };

strm << std::setprecision(std::numeric_limits<boost::decimal::decimal128_t>::digits10) << sqr;

BOOST_TEST(strm.str() == "1.000000000000000000000000000000005e+50");
}

const boost::decimal::decimal128_t cbr { cbrt(sum) };

{
std::stringstream strm { };

strm << std::setprecision(std::numeric_limits<boost::decimal::decimal128_t>::digits10)<< cbr;

BOOST_TEST(strm.str() == "2154434690031883721759293566519356");
}

const boost::decimal::decimal128_t lgt { log10(sum) };

{
std::stringstream strm { };

strm << std::setprecision(std::numeric_limits<boost::decimal::decimal128_t>::digits10)<< lgt;

BOOST_TEST(strm.str() == "100");
}
}

} // namespace local

auto main() -> int
{
local::test_near1();
local::test_large();

return boost::report_errors();
}