Skip to content
Merged
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
64 changes: 2 additions & 62 deletions include/boost/decimal/detail/cmath/ceil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,10 @@
#define BOOST_DECIMAL_DETAIL_CMATH_CEIL_HPP

#include <boost/decimal/fwd.hpp>
#include <boost/decimal/detail/config.hpp>
#include <boost/decimal/detail/type_traits.hpp>
#include <boost/decimal/detail/power_tables.hpp>
#include <boost/decimal/detail/apply_sign.hpp>
#include <boost/decimal/detail/concepts.hpp>
#include <boost/decimal/detail/cmath/impl/round_integral.hpp>
#include <boost/decimal/detail/config.hpp>
#include <boost/decimal/detail/cmath/fpclassify.hpp>
#include <boost/decimal/detail/cmath/frexp10.hpp>

#ifndef BOOST_DECIMAL_BUILD_MODULE
#include <type_traits>
Expand All @@ -27,63 +23,7 @@ BOOST_DECIMAL_EXPORT template <typename T>
constexpr auto ceil BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (const T val) noexcept
BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T)
{
using DivType = typename T::significand_type;
constexpr int inverse_eps {std::is_same<DivType, std::uint32_t>::value ? 7 :
std::is_same<DivType, std::uint64_t>::value ? 16 : 34};

constexpr T zero {0, 0};
constexpr T one {1, 0};
constexpr T max_comp_value {1u, inverse_eps};
const auto fp {fpclassify(val)};

switch (fp)
{
case FP_ZERO:
case FP_NAN:
case FP_INFINITE:
return val;
default:
static_cast<void>(val);
}

if (val > zero && val >= max_comp_value)
{
return val;
}

int exp_ptr {};
auto new_sig {frexp10(val, &exp_ptr)};
const auto abs_exp {detail::make_positive_unsigned(exp_ptr)};
const bool is_neg {val < zero};

const auto sig_dig {detail::precision_v<T>};
auto decimal_digits {static_cast<unsigned>(sig_dig)};
const auto zero_digits {detail::remove_trailing_zeros(new_sig).number_of_removed_zeros};
const auto non_zero_exp {exp_ptr + static_cast<int>(zero_digits)};

if (non_zero_exp >= 0)
{
// If the value is an integer, nothing should occur
return val;
}

if (sig_dig > abs_exp)
{
decimal_digits = abs_exp;
}
else if (exp_ptr < 1 && abs_exp >= sig_dig)
{
return is_neg ? zero : one;
}

new_sig /= detail::pow10<DivType>(decimal_digits);
if (!is_neg)
{
++new_sig;
}
new_sig *= 10U;

return T{new_sig, exp_ptr + static_cast<int>(decimal_digits) - 1, is_neg};
return detail::round_integral_impl(val, rounding_mode::fe_dec_upward);
}

} // namespace decimal
Expand Down
56 changes: 2 additions & 54 deletions include/boost/decimal/detail/cmath/floor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,8 @@
#include <boost/decimal/fwd.hpp>
#include <boost/decimal/detail/config.hpp>
#include <boost/decimal/detail/type_traits.hpp>
#include <boost/decimal/detail/power_tables.hpp>
#include <boost/decimal/detail/apply_sign.hpp>
#include <boost/decimal/detail/cmath/fpclassify.hpp>
#include <boost/decimal/detail/cmath/frexp10.hpp>
#include "../int128.hpp"
#include <boost/decimal/detail/concepts.hpp>
#include <boost/decimal/detail/construction_sign.hpp>
#include <boost/decimal/detail/cmath/impl/round_integral.hpp>

#ifndef BOOST_DECIMAL_BUILD_MODULE
#include <type_traits>
Expand All @@ -28,54 +23,7 @@ BOOST_DECIMAL_EXPORT template <typename T>
constexpr auto floor BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION (const T val) noexcept
BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T)
{
using DivType = typename T::significand_type;

constexpr T zero {0, 0};
constexpr T neg_one {1U, 0, construction_sign::negative};
const auto fp {fpclassify(val)};

switch (fp)
{
case FP_ZERO:
case FP_NAN:
case FP_INFINITE:
return val;
default:
static_cast<void>(val);
}

int exp_ptr {};
auto new_sig {frexp10(val, &exp_ptr)};
const auto abs_exp {detail::make_positive_unsigned(exp_ptr)};
const bool is_neg {val < zero};

const auto sig_dig {detail::precision_v<T>};
auto decimal_digits {static_cast<unsigned>(sig_dig)};
const auto zero_digits {detail::remove_trailing_zeros(new_sig).number_of_removed_zeros};
const auto non_zero_exp {exp_ptr + static_cast<int>(zero_digits)};

if (non_zero_exp >= 0)
{
// If the value is an integer, nothing should occur
return val;
}

if (sig_dig > abs_exp)
{
decimal_digits = abs_exp;
}
else if (exp_ptr < 1 && abs_exp >= sig_dig)
{
return is_neg ? neg_one : zero;
}

new_sig /= detail::pow10<DivType>(decimal_digits);
if (is_neg)
{
++new_sig;
}

return T{new_sig, exp_ptr + static_cast<int>(decimal_digits), is_neg};
return detail::round_integral_impl(val, rounding_mode::fe_dec_downward);
}

} // namespace decimal
Expand Down
100 changes: 100 additions & 0 deletions include/boost/decimal/detail/cmath/impl/round_integral.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2026 Shen-Ta Hsieh
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#ifndef BOOST_DECIMAL_DETAIL_CMATH_IMPL_ROUND_INTEGRAL_HPP
#define BOOST_DECIMAL_DETAIL_CMATH_IMPL_ROUND_INTEGRAL_HPP

#include <boost/decimal/detail/config.hpp>
#include <boost/decimal/detail/type_traits.hpp>
#include <boost/decimal/detail/attributes.hpp>
#include <boost/decimal/detail/power_tables.hpp>
#include <boost/decimal/detail/cmath/fpclassify.hpp>
#include <boost/decimal/detail/cmath/decompose.hpp>
#include <boost/decimal/detail/add_impl.hpp>
#include <boost/decimal/cfenv.hpp>

namespace boost {
namespace decimal {
namespace detail {

// Rounds c.sig * 10^c.exp, with c.exp < 0, to an integer in the mode round:
// one division by 10^-c.exp
template <typename T, typename Components>
BOOST_DECIMAL_FORCE_INLINE constexpr auto round_integral_sig(const Components& c, const rounding_mode round) noexcept -> typename T::significand_type
{
using sig_type = typename T::significand_type;

const auto shift {-static_cast<int>(c.exp)};
sig_type q {0U};
bool inc {};

if (shift > precision_v<T>)
{
// 0 < |x| < 0.1
inc = round == (c.sign ? rounding_mode::fe_dec_downward : rounding_mode::fe_dec_upward);
}
else
{
const auto p {pow10(static_cast<sig_type>(shift))};
q = static_cast<sig_type>(c.sig / p);
const auto r {static_cast<sig_type>(c.sig - q * p)};
if (r == 0U)
{
return q;
}

switch (round)
{
case rounding_mode::fe_dec_upward:
inc = !c.sign;
break;
case rounding_mode::fe_dec_downward:
inc = c.sign;
break;
case rounding_mode::fe_dec_to_nearest_from_zero:
inc = r >= p - r;
break;
case rounding_mode::fe_dec_to_nearest:
inc = r > p - r || (r == p - r && (static_cast<std::uint32_t>(q) & 1U) == 1U);
break;
case rounding_mode::fe_dec_toward_zero:
default:
break;
}
}

if (inc)
{
++q;
}

return q;
}

// Rounds x to an integer in the mode round. The result has exponent 0, and a zero result
// keeps the sign of x.
template <typename T>
constexpr auto round_integral_impl(const T x, const rounding_mode round) noexcept -> T
{
#ifndef BOOST_DECIMAL_FAST_MATH
if (!isfinite(x))
{
return x;
}
#endif

const auto c {decompose(x)};
if (c.exp >= 0 || c.sig == 0U)
{
return x;
}

return pack_in_range<T>(round_integral_sig<T>(c, round), 0, c.sign);
}

} // namespace detail
} // namespace decimal
} // namespace boost

#endif // BOOST_DECIMAL_DETAIL_CMATH_IMPL_ROUND_INTEGRAL_HPP
Loading
Loading