// Copyright 2023 Matt Borland // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt #ifndef BOOST_DECIMAL_DETAIL_TO_INTEGRAL_HPP #define BOOST_DECIMAL_DETAIL_TO_INTEGRAL_HPP #include #include #include #include #ifndef BOOST_DECIMAL_BUILD_MODULE #include #include #include #endif namespace boost { namespace decimal { // MSVC 14.1 warns of unary minus being applied to unsigned type from numeric_limits::min // 14.2 and on get it right #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4146) #endif // Attempts conversion to integral type: // If this is nan sets errno to EINVAL and returns 0 // If this is not representable sets errno to ERANGE and returns 0 template constexpr auto to_integral(Decimal val) noexcept BOOST_DECIMAL_REQUIRES_TWO_RETURN(detail::is_decimal_floating_point_v, Decimal, detail::is_integral_v, TargetType, TargetType) { using Conversion_Type = std::conditional_t::is_signed, std::int64_t, std::uint64_t>; constexpr Decimal max_target_type { (std::numeric_limits::max)() }; constexpr Decimal min_target_type { (std::numeric_limits::min)() }; constexpr Decimal one {1}; if (isnan(val)) { #if defined(__clang__) && __clang_major__ >= 20 if (!BOOST_DECIMAL_IS_CONSTANT_EVALUATED(val)) #endif { errno = EINVAL; } return static_cast(std::numeric_limits::max()); } // Anything in [0, 1) should be flushed to 0 if (abs(val) < one) { return static_cast(0); } if (val > max_target_type || val < min_target_type) { #if defined(__clang__) && __clang_major__ >= 20 if (!BOOST_DECIMAL_IS_CONSTANT_EVALUATED(val)) #endif { errno = ERANGE; } return static_cast(std::numeric_limits::max()); } auto result {static_cast(val.full_significand())}; auto expval {val.biased_exponent()}; const auto abs_exp_val {detail::make_positive_unsigned(expval)}; if (expval > 0) { if (expval > std::numeric_limits::digits10 + 1) { return std::numeric_limits::max(); } result *= detail::pow10(static_cast(expval)); } else if (expval < 0) { if (abs_exp_val > std::numeric_limits::digits10 + 1) { return static_cast(0); } result /= detail::pow10(static_cast(abs_exp_val)); } BOOST_DECIMAL_IF_CONSTEXPR (std::is_signed::value) { result = static_cast(val.isneg()) ? static_cast(detail::apply_sign(result)) : static_cast(result); } return static_cast(result); } template constexpr auto to_integral_128(Decimal val) noexcept BOOST_DECIMAL_REQUIRES_TWO_RETURN(detail::is_decimal_floating_point_v, Decimal, detail::is_integral_v, TargetType, TargetType) { constexpr Decimal max_target_type { (std::numeric_limits::max)() }; constexpr Decimal min_target_type { (std::numeric_limits::min)() }; constexpr Decimal one {1}; if (isnan(val)) { #if defined(__clang__) && __clang_major__ >= 20 if (!BOOST_DECIMAL_IS_CONSTANT_EVALUATED(val)) #endif { errno = EINVAL; } return static_cast(std::numeric_limits::max()); } // Anything in [0, 1) should be flushed to 0 if (abs(val) < one) { return static_cast(0); } if (val > max_target_type || val < min_target_type) { #if defined(__clang__) && __clang_major__ >= 20 if (!BOOST_DECIMAL_IS_CONSTANT_EVALUATED(val)) #endif { errno = ERANGE; } return static_cast(std::numeric_limits::max()); } auto sig {val.full_significand()}; auto expval {val.biased_exponent()}; const auto abs_exp_val {detail::make_positive_unsigned(expval)}; if (expval > 0) { if (expval > std::numeric_limits::digits) { return std::numeric_limits::max(); } sig *= detail::pow10(expval); } else if (expval < 0) { if (abs_exp_val > std::numeric_limits::digits) { return static_cast(0); } sig /= detail::pow10(abs_exp_val); } auto result {static_cast(sig)}; BOOST_DECIMAL_IF_CONSTEXPR (std::is_signed::value) { result = static_cast(val.isneg()) ? static_cast(detail::apply_sign(result)) : static_cast(result); } return result; } #ifdef _MSC_VER # pragma warning(pop) #endif } //namespace decimal } //namespace boost #endif //BOOST_DECIMAL_DETAIL_TO_INTEGRAL_HPP