diff --git a/include/boost/multiprecision/detail/default_ops.hpp b/include/boost/multiprecision/detail/default_ops.hpp index e77c8c91..dc9acfbe 100644 --- a/include/boost/multiprecision/detail/default_ops.hpp +++ b/include/boost/multiprecision/detail/default_ops.hpp @@ -385,7 +385,7 @@ inline void eval_multiply_default(T& t, const U& u, const V& v) } else { - t = u; + t = number::canonical_value(u); eval_multiply(t, v); } } @@ -395,13 +395,13 @@ inline void eval_multiply(T& t, const U& u, const V& v) eval_multiply_default(t, u, v); } -template -inline typename disable_if_c::value && is_same::value>::type eval_multiply_add(T& t, const U& u, const V& v, const X& x) +template +inline void eval_multiply_add(T& t, const T& u, const T& v, const T& x) { if((void*)&x == (void*)&t) { T z; - z = x; + z = number::canonical_value(x); eval_multiply_add(t, u, v, z); } else @@ -410,6 +410,25 @@ inline typename disable_if_c::value && is_same::value>::typ eval_add(t, x); } } + +template +inline typename boost::disable_if_c::value, T>::type make_T(const U& u) +{ + T t; + t = number::canonical_value(u); + return BOOST_MP_MOVE(t); +} +template +inline const T& make_T(const T& t) +{ + return t; +} + +template +inline typename disable_if_c::value && is_same::value>::type eval_multiply_add(T& t, const U& u, const V& v, const X& x) +{ + eval_multiply_add(t, make_T(u), make_T(v), make_T(x)); +} template inline typename enable_if_c::value && is_same::value>::type eval_multiply_add(T& t, const U& u, const V& v, const X& x) { @@ -1000,7 +1019,7 @@ inline typename boost::enable_if_c::value>::type eval_fd typedef typename boost::multiprecision::detail::canonical::type arithmetic_type; static const ui_type zero = 0u; arithmetic_type canonical_b = b; - switch(boost::math::fpclassify(b)) + switch((::boost::math::fpclassify)(b)) { case FP_NAN: case FP_INFINITE: @@ -1038,7 +1057,7 @@ inline typename boost::enable_if_c::value>::type eval_fd result = zero; return; } - switch(boost::math::fpclassify(a)) + switch((::boost::math::fpclassify)(a)) { case FP_NAN: result = zero; @@ -1055,12 +1074,6 @@ inline typename boost::enable_if_c::value>::type eval_fd result = zero; } -template -inline void eval_fma(T1& result, const T2& a, const T3& b, const T4& c) -{ -} - - template inline void eval_trunc(T& result, const T& a) { @@ -1512,11 +1525,11 @@ namespace multiprecision{ using boost::math::isnormal; typedef ::boost::math::policies::policy< - ::boost::math::policies::domain_error<::boost::math::policies::errno_on_error>, - ::boost::math::policies::pole_error<::boost::math::policies::errno_on_error>, - ::boost::math::policies::overflow_error<::boost::math::policies::errno_on_error>, - ::boost::math::policies::evaluation_error<::boost::math::policies::errno_on_error>, - ::boost::math::policies::rounding_error<::boost::math::policies::errno_on_error> + ::boost::math::policies::domain_error< ::boost::math::policies::errno_on_error>, + ::boost::math::policies::pole_error< ::boost::math::policies::errno_on_error>, + ::boost::math::policies::overflow_error< ::boost::math::policies::errno_on_error>, + ::boost::math::policies::evaluation_error< ::boost::math::policies::errno_on_error>, + ::boost::math::policies::rounding_error< ::boost::math::policies::errno_on_error> > c99_error_policy; template @@ -1955,6 +1968,200 @@ inline typename enable_if_c::value == number_kind_integer, nu eval_integer_sqrt(s.backend(), r.backend(), x.backend()); return s; } +// +// fma: +// + +namespace default_ops { + + struct fma_func + { + template + void operator()(B& result, const T& a, const U& b, const V& c)const + { + eval_multiply_add(result, a, b, c); + } + }; + + +} + +template +inline typename enable_if< + mpl::and_< + mpl::bool_ >::value == number_kind_floating_point>, + mpl::or_< + is_number, + is_number_expression, + is_arithmetic + >, + mpl::or_< + is_number, + is_number_expression, + is_arithmetic + > + >, + detail::expression, U, V> +>::type +fma(const number& a, const U& b, const V& c) +{ + return detail::expression, U, V>( + default_ops::fma_func(), a, b, c); +} + +template +inline typename enable_if< + mpl::and_< + mpl::bool_::result_type >::value == number_kind_floating_point>, + mpl::or_< + is_number, + is_number_expression, + is_arithmetic + >, + mpl::or_< + is_number, + is_number_expression, + is_arithmetic + > + >, + detail::expression, U, V> +>::type +fma(const detail::expression& a, const U& b, const V& c) +{ + return detail::expression, U, V>( + default_ops::fma_func(), a, b, c); +} + +template +inline typename enable_if< + mpl::and_< + mpl::bool_ >::value == number_kind_floating_point>, + mpl::or_< + is_number, + is_number_expression, + is_arithmetic + >, + mpl::or_< + is_number, + is_number_expression, + is_arithmetic + > + >, + number +>::type +fma(const number& a, const U& b, const V& c) +{ + using default_ops::eval_multiply_add; + number result; + eval_multiply_add(result.backend(), number::canonical_value(a), number::canonical_value(b), number::canonical_value(c)); + return BOOST_MP_MOVE(result); +} + +template +inline typename enable_if< + mpl::and_< + mpl::bool_ >::value == number_kind_floating_point>, + is_arithmetic, + mpl::or_< + is_number, + is_number_expression, + is_arithmetic + > + >, + detail::expression, V> +>::type +fma(const U& a, const number& b, const V& c) +{ + return detail::expression, V>( + default_ops::fma_func(), a, b, c); +} + +template +inline typename enable_if< + mpl::and_< + mpl::bool_::result_type >::value == number_kind_floating_point>, + is_arithmetic, + mpl::or_< + is_number, + is_number_expression, + is_arithmetic + > + >, + detail::expression, V> +>::type +fma(const U& a, const detail::expression& b, const V& c) +{ + return detail::expression, V>( + default_ops::fma_func(), a, b, c); +} + +template +inline typename enable_if< + mpl::and_< + mpl::bool_ >::value == number_kind_floating_point>, + is_arithmetic, + mpl::or_< + is_number, + is_number_expression, + is_arithmetic + > + >, + number +>::type +fma(const U& a, const number& b, const V& c) +{ + using default_ops::eval_multiply_add; + number result; + eval_multiply_add(result.backend(), number::canonical_value(a), number::canonical_value(b), number::canonical_value(c)); + return BOOST_MP_MOVE(result); +} + +template +inline typename enable_if< + mpl::and_< + mpl::bool_ >::value == number_kind_floating_point>, + is_arithmetic, + is_arithmetic + >, + detail::expression > +>::type +fma(const U& a, const V& b, const number& c) +{ + return detail::expression >( + default_ops::fma_func(), a, b, c); +} + +template +inline typename enable_if< + mpl::and_< + mpl::bool_::result_type >::value == number_kind_floating_point>, + is_arithmetic, + is_arithmetic + >, + detail::expression > +>::type +fma(const U& a, const V& b, const detail::expression& c) +{ + return detail::expression >( + default_ops::fma_func(), a, b, c); +} + +template +inline typename enable_if< + mpl::and_< + mpl::bool_ >::value == number_kind_floating_point>, + is_arithmetic, + is_arithmetic + >, + number +>::type +fma(const U& a, const V& b, const number& c) +{ + using default_ops::eval_multiply_add; + number result; + eval_multiply_add(result.backend(), number::canonical_value(a), number::canonical_value(b), number::canonical_value(c)); + return BOOST_MP_MOVE(result); +} template inline typename enable_if_c::value == number_kind_integer, number >::type diff --git a/include/boost/multiprecision/detail/integer_ops.hpp b/include/boost/multiprecision/detail/integer_ops.hpp index 58b1d75c..4b1a9d8b 100644 --- a/include/boost/multiprecision/detail/integer_ops.hpp +++ b/include/boost/multiprecision/detail/integer_ops.hpp @@ -469,7 +469,19 @@ inline typename enable_if< is_integral > >, - detail::expression >::type + typename mpl::if_< + is_no_et_number, + T, + typename mpl::if_< + is_no_et_number, + U, + typename mpl::if_< + is_no_et_number, + V, + detail::expression >::type + >::type + >::type + >::type powm(const T& b, const U& p, const V& mod) { return detail::expression( diff --git a/include/boost/multiprecision/detail/number_base.hpp b/include/boost/multiprecision/detail/number_base.hpp index 5911b3a2..9b10dd82 100644 --- a/include/boost/multiprecision/detail/number_base.hpp +++ b/include/boost/multiprecision/detail/number_base.hpp @@ -74,6 +74,18 @@ struct is_number : public mpl::false_ {}; template struct is_number > : public mpl::true_ {}; +template +struct is_et_number : public mpl::false_ {}; + +template +struct is_et_number > : public mpl::true_ {}; + +template +struct is_no_et_number : public mpl::false_ {}; + +template +struct is_no_et_number > : public mpl::true_ {}; + namespace detail{ // Forward-declare an expression wrapper @@ -696,11 +708,11 @@ struct expression typedef typename right_middle_type::result_type right_middle_result_type; typedef typename right_type::result_type right_result_type; typedef typename combine_expression< + left_result_type, typename combine_expression< - typename combine_expression::type, - right_middle_result_type - >::type, - right_result_type + left_middle_result_type, + typename combine_expression::type + >::type >::type result_type; typedef tag tag_type; diff --git a/include/boost/multiprecision/float128.hpp b/include/boost/multiprecision/float128.hpp index ee5042e2..6b6afdc3 100644 --- a/include/boost/multiprecision/float128.hpp +++ b/include/boost/multiprecision/float128.hpp @@ -489,6 +489,10 @@ inline void eval_atan2(float128_backend& result, const float128_backend& a, cons { result.value() = atan2q(a.value(), b.value()); } +inline void eval_multiply_add(float128_backend& result, const float128_backend& a, const float128_backend& b, const float128_backend& c) +{ + result.value() = fmaq(a.value(), b.value(), c.value()); +} inline std::size_t hash_value(const float128_backend& val) { diff --git a/include/boost/multiprecision/mpfr.hpp b/include/boost/multiprecision/mpfr.hpp index c18ba5b6..fb088da8 100644 --- a/include/boost/multiprecision/mpfr.hpp +++ b/include/boost/multiprecision/mpfr.hpp @@ -1452,6 +1452,31 @@ inline void eval_fmod(mpfr_float_backend& result, const mpfr_fmod(result.data(), a.data(), b.data(), GMP_RNDN); } +template +inline void eval_multiply_add(mpfr_float_backend& result, const mpfr_float_backend& a, const mpfr_float_backend& b) +{ + mpfr_fma(result.data(), a.data(), b.data(), result.data(), GMP_RNDN); +} + +template +inline void eval_multiply_add(mpfr_float_backend& result, const mpfr_float_backend& a, const mpfr_float_backend& b, const mpfr_float_backend& c) +{ + mpfr_fma(result.data(), a.data(), b.data(), c.data(), GMP_RNDN); +} + +template +inline void eval_multiply_subtract(mpfr_float_backend& result, const mpfr_float_backend& a, const mpfr_float_backend& b) +{ + mpfr_fms(result.data(), a.data(), b.data(), result.data(), GMP_RNDN); + result.negate(); +} + +template +inline void eval_multiply_subtract(mpfr_float_backend& result, const mpfr_float_backend& a, const mpfr_float_backend& b, const mpfr_float_backend& c) +{ + mpfr_fms(result.data(), a.data(), b.data(), c.data(), GMP_RNDN); +} + template inline std::size_t hash_value(const mpfr_float_backend& val) { diff --git a/test/test_arithmetic.hpp b/test/test_arithmetic.hpp index e9c8ab7a..5ad80033 100644 --- a/test/test_arithmetic.hpp +++ b/test/test_arithmetic.hpp @@ -742,7 +742,7 @@ void test_float_funcs(const boost::mpl::true_&) // // Test variable reuse in function calls, see https://svn.boost.org/trac/boost/ticket/8326 // - Real a(2), b(10), c; + Real a(2), b(10), c, d; a = pow(a, b); BOOST_CHECK_EQUAL(a, 1024); a = 2; @@ -898,6 +898,25 @@ void test_float_funcs(const boost::mpl::true_&) a = 4; b = atan2(a, b); BOOST_CHECK_CLOSE_FRACTION(b, Real(atan2(Real(4), Real(2))), tol); + + // fma: + a = 2; + b = 4; + c = 6; + BOOST_CHECK_EQUAL(fma(a, b, c), 14); + BOOST_CHECK_EQUAL(fma(a, 4, c), 14); + BOOST_CHECK_EQUAL(fma(a, b, 6), 14); + BOOST_CHECK_EQUAL(fma(a, 4, 6), 14); + BOOST_CHECK_EQUAL(fma(a + 0, b, c), 14); + BOOST_CHECK_EQUAL(fma(a - 0, 4, c), 14); + BOOST_CHECK_EQUAL(fma(a * 1, b, 6), 14); + BOOST_CHECK_EQUAL(fma(a / 1, 4, 6), 14); + BOOST_CHECK_EQUAL(fma(2, b, c), 14); + BOOST_CHECK_EQUAL(fma(2, b, 6), 14); + BOOST_CHECK_EQUAL(fma(2, b * 1, c), 14); + BOOST_CHECK_EQUAL(fma(2, b + 0, 6), 14); + BOOST_CHECK_EQUAL(fma(2, 4, c), 14); + BOOST_CHECK_EQUAL(fma(2, 4, c + 0), 14); } template