c++boostmultiprecision

Boost multiprecision rounding towards 0


The code below compiles just fine on Windows using VC++:

#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>


#include <iostream>

namespace mp = boost::multiprecision;
using BigFloat = mp::cpp_dec_float_50;
using BigInt = mp::uint256_t;

template <int decimals = 0, typename T> T floorBI(T const& v)
{
    static const T scale = pow(T(10), decimals);

    if (v.is_zero())
        return v;

    // ceil/floor is found via ADL and uses expression templates for
    // optimization
    if (v < 0)
        return ceil(v * scale) / scale;
    else
        // floor is found via ADL and uses expression templates for optimization
        return floor(v * scale) / scale;
}

int main()
{
    BigFloat A = 3;
    BigFloat B = 2; 

 static_cast<BigInt>(floorBI<0>(static_cast<BigFloat>(A) / static_cast<BigFloat>(B)));
    return 0;
}

, however with GCC (for Android and iOS) there's a problem seemingly with templates.

The particular error(s) for a template which 'should' be matched read(s)

..\boost/multiprecision/detail/default_ops.hpp:3745:18: note: candidate template ignored: could not match 'expression' against 'number' UNARY_OP_FUNCTOR(ceil, number_kind_floating_point) ^ ..\boost/multiprecision/detail/default_ops.hpp:3745:18: note: candidate template ignored: could not match 1 against 0 ..\boost/multiprecision/detail/default_ops.hpp:3745:18: note: candidate template ignored: requirement 'boost::multiprecision::number_category<boost::multiprecision::backends::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void> >::value == number_kind_floating_point' was not satisfied [with Backend = boost::multiprecision::backends::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>] ..\boost/multiprecision/detail/default_ops.hpp:3745:18: note: candidate template ignored: could not match 1 against 0

VC++ handles all of it 'just fine'.

Waiting for @Sehe to come around;]


Solution

  • Like many people already suggested, expression templates are the culprit:

    T is deduced as

    boost::multiprecision::detail::expression<
        boost::multiprecision::detail::divides,
        boost::multiprecision::number<
            boost::multiprecision::backends::cpp_dec_float<50>>,
        boost::multiprecision::detail::expression<
            boost::multiprecision::detail::multiply_immediates,
            boost::multiprecision::number<
                boost::multiprecision::backends::cpp_dec_float<50>>,
            boost::multiprecision::number<
                boost::multiprecision::backends::cpp_dec_float<50>>,
            void, void>,
        void, void>
    

    Which is not constructible from int: T(10). Instead, either force the type:

    std::cout << floorBI<0>(BigFloat(A / (B * C))) << "\n";
    

    Or be smarter about deducing the type.

    DEMONSTRUCTION

    Live On Coliru

    #include <boost/multiprecision/cpp_dec_float.hpp>
    #include <boost/multiprecision/cpp_int.hpp>
    #include <boost/multiprecision/number.hpp>
    #include <iostream>
    
    namespace bmp = boost::multiprecision;
    using BigFloat = bmp::cpp_dec_float_50;
    
    template <int decimals = 0, typename T>
    std::enable_if_t<not bmp::is_number_expression<T>::value, T>
    floorBI(T const& v)
    {
        static const T scale = pow(T(10), decimals);
    
        if (v.is_zero())
            return v;
    
        // ceil/floor is found via ADL and uses expression templates for
        // optimization
        if (v < 0)
            return ceil(v * scale) / scale;
        else
            // floor is found via ADL and uses expression templates for optimization
            return floor(v * scale) / scale;
    }
    
    template <int decimals = 0, typename Expr>
    auto floorBI(Expr const& expr,
        std::enable_if_t<bmp::is_number_expression<Expr>::value, void>* enable = nullptr)
    {
        return floorBI<decimals, typename Expr::result_type>(expr);
    }
    
    
    int main()
    {
        BigFloat A(3), B(2), C(2);
        std::cout <<  floorBI<1>(BigFloat(A / (B * C))) << "\n";
        std::cout <<  floorBI<1>(A / (B * C)) << "\n";
        std::cout <<  floorBI<2>(BigFloat(A / (B * C))) << "\n";
        std::cout <<  floorBI<2>(A / (B * C)) << "\n";
        B *= -1;
        std::cout <<  floorBI<1>(BigFloat(A / (B * C))) << "\n";
        std::cout <<  floorBI<1>(A / (B * C)) << "\n";
        std::cout <<  floorBI<2>(BigFloat(A / (B * C))) << "\n";
        std::cout <<  floorBI<2>(A / (B * C)) << "\n";
    }
    

    Prints

    0.7
    0.7
    0.75
    0.75
    -0.7
    -0.7
    -0.75
    -0.75