// (c) Copyright Fernando Luis Cacciola Carballal 2000-2004 // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // See library home page at http://www.boost.org/libs/numeric/conversion // // Contact the author at: fernando_cacciola@hotmail.com // #ifndef BOOST_NUMERIC_CONVERSION_CONVERTER_POLICIES_FLC_12NOV2002_HPP #define BOOST_NUMERIC_CONVERSION_CONVERTER_POLICIES_FLC_12NOV2002_HPP #include #include // for std::bad_cast #include #include #include // for std::floor and std::ceil #include #include #include #include namespace boost { namespace numeric { template struct Trunc { typedef S source_type ; typedef typename mpl::if_< is_arithmetic,S,S const&>::type argument_type ; static source_type nearbyint ( argument_type s ) { #if !defined(BOOST_NO_STDC_NAMESPACE) using std::floor ; using std::ceil ; #endif return s < static_cast(0) ? ceil(s) : floor(s) ; } typedef mpl::integral_c< std::float_round_style, std::round_toward_zero> round_style ; } ; template struct Floor { typedef S source_type ; typedef typename mpl::if_< is_arithmetic,S,S const&>::type argument_type ; static source_type nearbyint ( argument_type s ) { #if !defined(BOOST_NO_STDC_NAMESPACE) using std::floor ; #endif return floor(s) ; } typedef mpl::integral_c< std::float_round_style, std::round_toward_neg_infinity> round_style ; } ; template struct Ceil { typedef S source_type ; typedef typename mpl::if_< is_arithmetic,S,S const&>::type argument_type ; static source_type nearbyint ( argument_type s ) { #if !defined(BOOST_NO_STDC_NAMESPACE) using std::ceil ; #endif return ceil(s) ; } typedef mpl::integral_c< std::float_round_style, std::round_toward_infinity> round_style ; } ; template struct RoundEven { typedef S source_type ; typedef typename mpl::if_< is_arithmetic,S,S const&>::type argument_type ; static source_type nearbyint ( argument_type s ) { // Algorithm contributed by Guillaume Melquiond #if !defined(BOOST_NO_STDC_NAMESPACE) using std::floor ; using std::ceil ; #endif // only works inside the range not at the boundaries S prev = floor(s); S next = ceil(s); S rt = (s - prev) - (next - s); // remainder type S const zero(0.0); S const two(2.0); if ( rt < zero ) return prev; else if ( rt > zero ) return next; else { bool is_prev_even = two * floor(prev / two) == prev ; return ( is_prev_even ? prev : next ) ; } } typedef mpl::integral_c< std::float_round_style, std::round_to_nearest> round_style ; } ; enum range_check_result { cInRange = 0 , cNegOverflow = 1 , cPosOverflow = 2 } ; class bad_numeric_cast : public std::bad_cast { public: const char * what() const BOOST_NOEXCEPT_OR_NOTHROW BOOST_OVERRIDE { return "bad numeric conversion: overflow"; } }; class negative_overflow : public bad_numeric_cast { public: const char * what() const BOOST_NOEXCEPT_OR_NOTHROW BOOST_OVERRIDE { return "bad numeric conversion: negative overflow"; } }; class positive_overflow : public bad_numeric_cast { public: const char * what() const BOOST_NOEXCEPT_OR_NOTHROW BOOST_OVERRIDE { return "bad numeric conversion: positive overflow"; } }; struct def_overflow_handler { void operator() ( range_check_result r ) // throw(negative_overflow,positive_overflow) { #ifndef BOOST_NO_EXCEPTIONS if ( r == cNegOverflow ) throw negative_overflow() ; else if ( r == cPosOverflow ) throw positive_overflow() ; #else if ( r == cNegOverflow ) ::boost::throw_exception(negative_overflow()) ; else if ( r == cPosOverflow ) ::boost::throw_exception(positive_overflow()) ; #endif } } ; struct silent_overflow_handler { void operator() ( range_check_result ) {} // throw() } ; template struct raw_converter { protected: // https://stackoverflow.com/questions/41144668/how-to-efficiently-perform-double-int64-conversions-with-sse-avx static constexpr float64_t mask_f64_hi_sint64 = 19342822337206103650074624.0, // exp2(52+32) + exp2(63) mask_f64_hi_uint64 = 19342813113834066795298816.0, // exp2(52+32) mask_f64_lo_int64 = 4503599627370496.0; // exp2(52) template [[nodiscard]] inline static constexpr float64_t cvt_int64_float64(const Type arg) noexcept { static_assert ( std::is_same_v || std::is_same_v ); constexpr float64_t mask_hi = std::is_signed_v ? mask_f64_hi_sint64 : mask_f64_hi_uint64, mask_lo = mask_f64_lo_int64, magic = mask_hi + mask_lo; const uint64_t hi = std::bit_cast(mask_hi) ^ (uint64_t(arg) >> 32), lo = std::bit_cast(mask_lo) | (uint64_t(arg) & 0xffffffffull); return (std::bit_cast(hi) - magic) + std::bit_cast(lo); } public: using argument_type = typename Traits::argument_type; using result_type = typename Traits::result_type; [[nodiscard]] inline static constexpr result_type low_level_convert(const argument_type s) noexcept { #if defined(BOOST_ARCH_X86) && !defined(__AVX512DQ__) if constexpr (std::is_same_v || std::is_same_v) { if constexpr (std::is_same_v) return cvt_int64_float64(s); else if constexpr (std::is_same_v) // todo optimize return result_type(cvt_int64_float64(s)); else return result_type(s); } else return result_type(s); #else return result_type(s); #endif } }; struct UseInternalRangeChecker {} ; } } // namespace boost::numeric #endif