Add increment, decrement and bitwise-shift operators.

[SVN r74024]
This commit is contained in:
John Maddock
2011-08-23 18:31:03 +00:00
parent 5dfb34d1bd
commit cdcf165e30
6 changed files with 248 additions and 4 deletions

View File

@@ -17,6 +17,9 @@
#include <boost/type_traits/is_signed.hpp>
#include <boost/type_traits/is_unsigned.hpp>
#include <boost/type_traits/is_floating_point.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/make_unsigned.hpp>
#include <boost/throw_exception.hpp>
#include <boost/math/big_number/default_ops.hpp>
namespace boost{ namespace math{
@@ -201,6 +204,65 @@ public:
}
return *this;
}
//
// These operators are *not* proto-ized.
// The issue is that the increment/decrement must happen
// even if the result of the operator *is never used*.
// Possibly we could modify our expression wrapper to
// execute the increment/decrement on destruction, but
// correct implemetation will be tricky, so defered for now...
//
big_number& operator++()
{
BOOST_STATIC_ASSERT_MSG(is_extended_integer<Backend>::value, "The increment operation is only valid for integer types");
using big_num_default_ops::increment;
increment(m_backend);
return *this;
}
big_number& operator--()
{
BOOST_STATIC_ASSERT_MSG(is_extended_integer<Backend>::value, "The increment operation is only valid for integer types");
using big_num_default_ops::decrement;
decrement(m_backend);
return *this;
}
big_number operator++(int)
{
BOOST_STATIC_ASSERT_MSG(is_extended_integer<Backend>::value, "The increment operation is only valid for integer types");
using big_num_default_ops::increment;
self_type temp(*this);
increment(m_backend);
return temp;
}
big_number operator--(int)
{
BOOST_STATIC_ASSERT_MSG(is_extended_integer<Backend>::value, "The increment operation is only valid for integer types");
using big_num_default_ops::decrement;
self_type temp(*this);
decrement(m_backend);
return temp;
}
template <class V>
typename enable_if<is_integral<V>, big_number&>::type operator <<= (V val)
{
BOOST_STATIC_ASSERT_MSG(is_extended_integer<Backend>::value, "The left-shift operation is only valid for integer types");
check_shift_range(val, mpl::bool_<(sizeof(V) > sizeof(std::size_t))>(), is_signed<V>());
left_shift(m_backend, canonical_value(val));
return *this;
}
template <class V>
typename enable_if<is_integral<V>, big_number&>::type operator >>= (V val)
{
BOOST_STATIC_ASSERT_MSG(is_extended_integer<Backend>::value, "The right-shift operation is only valid for integer types");
check_shift_range(val, mpl::bool_<(sizeof(V) > sizeof(std::size_t))>(), is_signed<V>());
right_shift(m_backend, canonical_value(val));
return *this;
}
template <class V>
typename enable_if<boost::is_arithmetic<V>, big_number<Backend>& >::type
@@ -237,6 +299,14 @@ public:
return *this;
}
//
// swap:
//
void swap(self_type& other)
{
m_backend.swap(other.backend());
}
//
// String conversion functions:
//
@@ -284,6 +354,29 @@ public:
return m_backend;
}
private:
template <class V>
void check_shift_range(V val, const mpl::true_&, const mpl::true_&)
{
if(val > std::numeric_limits<std::size_t>::max())
BOOST_THROW_EXCEPTION(std::out_of_range("Can not shift by a value greater than std::numeric_limits<std::size_t>::max()."));
if(val < 0)
BOOST_THROW_EXCEPTION(std::out_of_range("Can not shift by a negative value."));
}
template <class V>
void check_shift_range(V val, const mpl::false_&, const mpl::true_&)
{
if(val < 0)
BOOST_THROW_EXCEPTION(std::out_of_range("Can not shift by a negative value."));
}
template <class V>
void check_shift_range(V val, const mpl::true_&, const mpl::false_&)
{
if(val > std::numeric_limits<std::size_t>::max())
BOOST_THROW_EXCEPTION(std::out_of_range("Can not shift by a value greater than std::numeric_limits<std::size_t>::max()."));
}
template <class V>
void check_shift_range(V val, const mpl::false_&, const mpl::false_&){}
template <class Exp>
void do_assign(const Exp& e, const detail::add_immediates&)
{
@@ -363,6 +456,7 @@ private:
template <class Exp>
void do_assign(const Exp& e, const detail::modulus_immediates&)
{
BOOST_STATIC_ASSERT_MSG(is_extended_integer<Backend>::value, "The modulus operation is only valid for integer types");
using big_num_default_ops::modulus;
typedef typename proto::tag_of<typename proto::result_of::left<Exp>::type>::type left_tag;
typedef typename proto::tag_of<typename proto::result_of::right<Exp>::type>::type right_tag;
@@ -575,6 +669,64 @@ private:
typedef typename proto::arity_of<Exp>::type tag_type;
do_assign_function(e, tag_type());
}
template <class Exp>
void do_assign(const Exp& e, const proto::tag::shift_left&)
{
// We can only shift by an integer value, not an arbitrary expression:
typedef typename proto::result_of::left<Exp>::type left_type;
typedef typename proto::result_of::right<Exp>::type right_type;
typedef typename proto::arity_of<right_type>::type right_arity;
BOOST_STATIC_ASSERT_MSG(right_arity::value == 0, "The left shift operator requires an integer value for the shift operand.");
typedef typename proto::result_of::value<right_type>::type right_value_type;
BOOST_STATIC_ASSERT_MSG(is_integral<right_value_type>::value, "The left shift operator requires an integer value for the shift operand.");
typedef typename proto::tag_of<left_type>::type tag_type;
do_assign_left_shift(proto::left(e), canonical_value(proto::value(proto::right(e))), tag_type());
}
template <class Exp>
void do_assign(const Exp& e, const proto::tag::shift_right&)
{
// We can only shift by an integer value, not an arbitrary expression:
typedef typename proto::result_of::left<Exp>::type left_type;
typedef typename proto::result_of::right<Exp>::type right_type;
typedef typename proto::arity_of<right_type>::type right_arity;
BOOST_STATIC_ASSERT_MSG(right_arity::value == 0, "The left shift operator requires an integer value for the shift operand.");
typedef typename proto::result_of::value<right_type>::type right_value_type;
BOOST_STATIC_ASSERT_MSG(is_integral<right_value_type>::value, "The left shift operator requires an integer value for the shift operand.");
typedef typename proto::tag_of<left_type>::type tag_type;
do_assign_right_shift(proto::left(e), canonical_value(proto::value(proto::right(e))), tag_type());
}
template <class Exp, class Val>
void do_assign_right_shift(const Exp& e, const Val& val, const proto::tag::terminal&)
{
using big_num_default_ops::right_shift;
right_shift(m_backend, canonical_value(proto::value(e)), val);
}
template <class Exp, class Val>
void do_assign_left_shift(const Exp& e, const Val& val, const proto::tag::terminal&)
{
using big_num_default_ops::left_shift;
left_shift(m_backend, canonical_value(proto::value(e)), val);
}
template <class Exp, class Val, class Tag>
void do_assign_right_shift(const Exp& e, const Val& val, const Tag&)
{
using big_num_default_ops::right_shift;
self_type temp(e);
right_shift(m_backend, temp.backend(), val);
}
template <class Exp, class Val, class Tag>
void do_assign_left_shift(const Exp& e, const Val& val, const Tag&)
{
using big_num_default_ops::left_shift;
self_type temp(e);
left_shift(m_backend, temp.backend(), val);
}
template <class Exp>
void do_assign_function(const Exp& e, const mpl::long_<1>&)
{
@@ -804,6 +956,7 @@ private:
template <class Exp>
void do_modulus(const Exp& e, const proto::tag::terminal&)
{
BOOST_STATIC_ASSERT_MSG(is_extended_integer<Backend>::value, "The modulus operation is only valid for integer types");
using big_num_default_ops::modulus;
modulus(m_backend, canonical_value(proto::value(e)));
}
@@ -811,6 +964,7 @@ private:
template <class Exp, class Unknown>
void do_modulus(const Exp& e, const Unknown&)
{
BOOST_STATIC_ASSERT_MSG(is_extended_integer<Backend>::value, "The modulus operation is only valid for integer types");
using big_num_default_ops::modulus;
self_type temp(e);
modulus(m_backend, canonical_value(proto::value(temp)));

View File

@@ -29,6 +29,8 @@ struct big_number_grammar
, proto::unary_plus< big_number_grammar >
, proto::negate< big_number_grammar >
, proto::modulus<big_number_grammar, big_number_grammar>
, proto::shift_left<big_number_grammar, big_number_grammar>
, proto::shift_right<big_number_grammar, big_number_grammar>
>
{};

View File

@@ -146,6 +146,34 @@ inline void modulus(T& t, const U& u, const V& v)
}
}
template <class T>
inline void increment(T& val)
{
typedef typename mpl::front<typename T::unsigned_types>::type ui_type;
add(val, static_cast<ui_type>(1u));
}
template <class T>
inline void decrement(T& val)
{
typedef typename mpl::front<typename T::unsigned_types>::type ui_type;
subtract(val, static_cast<ui_type>(1u));
}
template <class T, class V>
inline void left_shift(T& result, const T& arg, const V val)
{
result = arg;
left_shift(result, val);
}
template <class T, class V>
inline void right_shift(T& result, const T& arg, const V val)
{
result = arg;
right_shift(result, val);
}
//
// Functions:
//

View File

@@ -862,6 +862,26 @@ inline void divide(gmp_int& t, long i)
if(i < 0)
mpz_neg(t.data(), t.data());
}
template <class UI>
inline void left_shift(gmp_int& t, UI i)
{
mpz_mul_2exp(t.data(), t.data(), static_cast<mp_bitcnt_t>(i));
}
template <class UI>
inline void right_shift(gmp_int& t, UI i)
{
mpz_fdiv_q_2exp(t.data(), t.data(), static_cast<mp_bitcnt_t>(i));
}
template <class UI>
inline void left_shift(gmp_int& t, const gmp_int& v, UI i)
{
mpz_mul_2exp(t.data(), v.data(), static_cast<mp_bitcnt_t>(i));
}
template <class UI>
inline void right_shift(gmp_int& t, const gmp_int& v, UI i)
{
mpz_fdiv_q_2exp(t.data(), v.data(), static_cast<mp_bitcnt_t>(i));
}
inline void add(gmp_int& t, const gmp_int& p, const gmp_int& o)
{
@@ -1087,8 +1107,8 @@ private:
{
std::numeric_limits<boost::math::big_number<boost::math::gmp_real<digits10> > >::epsilon();
std::numeric_limits<boost::math::big_number<boost::math::gmp_real<digits10> > >::round_error();
std::numeric_limits<boost::math::big_number<boost::math::gmp_real<digits10> > >::min();
std::numeric_limits<boost::math::big_number<boost::math::gmp_real<digits10> > >::max();
(std::numeric_limits<boost::math::big_number<boost::math::gmp_real<digits10> > >::min)();
(std::numeric_limits<boost::math::big_number<boost::math::gmp_real<digits10> > >::max)();
}
void do_nothing()const{}
};

View File

@@ -741,8 +741,8 @@ private:
{
std::numeric_limits<boost::math::big_number<boost::math::mpfr_real_backend<digits10> > >::epsilon();
std::numeric_limits<boost::math::big_number<boost::math::mpfr_real_backend<digits10> > >::round_error();
std::numeric_limits<boost::math::big_number<boost::math::mpfr_real_backend<digits10> > >::min();
std::numeric_limits<boost::math::big_number<boost::math::mpfr_real_backend<digits10> > >::max();
(std::numeric_limits<boost::math::big_number<boost::math::mpfr_real_backend<digits10> > >::min)();
(std::numeric_limits<boost::math::big_number<boost::math::mpfr_real_backend<digits10> > >::max)();
std::numeric_limits<boost::math::big_number<boost::math::mpfr_real_backend<digits10> > >::infinity();
std::numeric_limits<boost::math::big_number<boost::math::mpfr_real_backend<digits10> > >::quiet_NaN();
}

View File

@@ -36,6 +36,11 @@
#include <boost/math/big_number/mpfr.hpp>
#endif
#define BOOST_TEST_THROW(x, EX)\
try { x; BOOST_ERROR("Expected exception not thrown"); } \
catch(const EX&){}\
catch(...){ BOOST_ERROR("Incorrect exception type thrown"); }
template <class Real>
void test_integer_ops(const boost::mpl::false_&){}
@@ -104,6 +109,41 @@ void test_integer_ops(const boost::mpl::true_&)
a %= -7LL;
BOOST_TEST(a == -20 % -7);
#endif
a = 20;
BOOST_TEST(++a == 21);
BOOST_TEST(--a == 20);
BOOST_TEST(a++ == 20);
BOOST_TEST(a == 21);
BOOST_TEST(a-- == 21);
BOOST_TEST(a == 20);
a = 2000;
a <<= 20;
BOOST_TEST(a == 2000L << 20);
a >>= 20;
BOOST_TEST(a == 2000);
a <<= 20u;
BOOST_TEST(a == 2000L << 20);
a >>= 20u;
BOOST_TEST(a == 2000);
BOOST_TEST_THROW(a <<= -20, std::out_of_range);
BOOST_TEST_THROW(a >>= -20, std::out_of_range);
#ifndef BOOST_NO_LONG_LONG
if(sizeof(long long) > sizeof(std::size_t))
{
// extreme values should trigger an exception:
BOOST_TEST_THROW(a >>= (1uLL << (sizeof(long long) * CHAR_BIT - 2)), std::out_of_range);
BOOST_TEST_THROW(a <<= (1uLL << (sizeof(long long) * CHAR_BIT - 2)), std::out_of_range);
}
#endif
a = 20;
b = a << 20;
BOOST_TEST(b == (20 << 20));
b = a >> 2;
BOOST_TEST(b == (20 >> 2));
b = (a + 2) << 10;
BOOST_TEST(b == (22 << 10));
b = (a + 3) >> 3;
BOOST_TEST(b == (23 >> 3));
//
// Non-member functions:
//