Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r77630 - in sandbox/big_number: boost/multiprecision boost/multiprecision/detail libs/multiprecision/test
From: john_at_[hidden]
Date: 2012-03-29 14:00:08


Author: johnmaddock
Date: 2012-03-29 14:00:06 EDT (Thu, 29 Mar 2012)
New Revision: 77630
URL: http://svn.boost.org/trac/boost/changeset/77630

Log:
Change cpp_dec_float string conversion to generate a better error message when conversion fails.
Add generic inter-conversions and tests for them.
Fix bug in cpp_int convert_to.
Added:
   sandbox/big_number/boost/multiprecision/detail/generic_interconvert.hpp (contents, props changed)
   sandbox/big_number/libs/multiprecision/test/test_generic_conv.cpp (contents, props changed)
Text files modified:
   sandbox/big_number/boost/multiprecision/cpp_dec_float.hpp | 14 +++++++++++++-
   sandbox/big_number/boost/multiprecision/cpp_int.hpp | 4 +---
   sandbox/big_number/boost/multiprecision/mp_number.hpp | 21 ++++++++++++++++++++-
   sandbox/big_number/libs/multiprecision/test/Jamfile.v2 | 11 +++++++++++
   4 files changed, 45 insertions(+), 5 deletions(-)

Modified: sandbox/big_number/boost/multiprecision/cpp_dec_float.hpp
==============================================================================
--- sandbox/big_number/boost/multiprecision/cpp_dec_float.hpp (original)
+++ sandbox/big_number/boost/multiprecision/cpp_dec_float.hpp 2012-03-29 14:00:06 EDT (Thu, 29 Mar 2012)
@@ -1804,6 +1804,8 @@
 template <unsigned Digits10>
 bool cpp_dec_float<Digits10>::rd_string(const char* const s)
 {
+ try{
+
    std::string str(s);
 
    // Get a possible exponent and remove it.
@@ -1849,7 +1851,7 @@
          *this = this->inf();
       return true;
    }
- if((str.size() >= 3) && ((str.substr(0, 3) == "nan") || (str.substr(0, 3) == "NAN")))
+ if((str.size() >= 3) && ((str.substr(0, 3) == "nan") || (str.substr(0, 3) == "NAN") || (str.substr(0, 3) == "NaN")))
    {
       *this = this->nan();
       return true;
@@ -2056,6 +2058,16 @@
       }
    }
 
+ }
+ catch(const bad_lexical_cast&)
+ {
+ // Rethrow with better error message:
+ std::string msg = "Unable to parse the string \"";
+ msg += s;
+ msg += "\" as a floating point value.";
+ throw std::runtime_error(msg);
+ }
+
    return true;
 }
 

Modified: sandbox/big_number/boost/multiprecision/cpp_int.hpp
==============================================================================
--- sandbox/big_number/boost/multiprecision/cpp_int.hpp (original)
+++ sandbox/big_number/boost/multiprecision/cpp_int.hpp 2012-03-29 14:00:06 EDT (Thu, 29 Mar 2012)
@@ -2025,12 +2025,10 @@
 {
    *result = static_cast<R>(backend.limbs()[0]);
    unsigned shift = cpp_int_backend<MinBits, Signed, Allocator>::limb_bits;
- for(unsigned i = 1; i < backend.size(); ++i)
+ for(unsigned i = 1; (i < backend.size()) && (shift < static_cast<unsigned>(std::numeric_limits<R>::digits)); ++i)
    {
       *result += static_cast<R>(backend.limbs()[i]) << shift;
       shift += cpp_int_backend<MinBits, Signed, Allocator>::limb_bits;
- if(shift > static_cast<unsigned>(std::numeric_limits<R>::digits))
- break;
    }
    if(backend.sign())
    {

Added: sandbox/big_number/boost/multiprecision/detail/generic_interconvert.hpp
==============================================================================
--- (empty file)
+++ sandbox/big_number/boost/multiprecision/detail/generic_interconvert.hpp 2012-03-29 14:00:06 EDT (Thu, 29 Mar 2012)
@@ -0,0 +1,221 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright 2011 John Maddock. Distributed under 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)
+
+#ifndef BOOST_MP_GENERIC_INTERCONVERT_HPP
+#define BOOST_MP_GENERIC_INTERCONVERT_HPP
+
+#include <boost/multiprecision/detail/default_ops.hpp>
+
+namespace boost{ namespace multiprecision{ namespace detail{
+
+template <class To, class From>
+void generic_interconvert(To& to, const From& from, const mpl::int_<number_kind_floating_point>& /*to_type*/, const mpl::int_<number_kind_integer>& /*from_type*/)
+{
+ using default_ops::eval_get_sign;
+ using default_ops::eval_bitwise_and;
+ using default_ops::eval_convert_to;
+ using default_ops::eval_right_shift;
+ using default_ops::eval_ldexp;
+ using default_ops::eval_add;
+ // smallest unsigned type handled natively by "From" is likely to be it's limb_type:
+ typedef typename canonical<unsigned char, From>::type limb_type;
+ // get the corresponding type that we can assign to "To":
+ typedef typename canonical<limb_type, To>::type to_type;
+ From t(from);
+ bool is_neg = eval_get_sign(t) < 0;
+ if(is_neg)
+ t.negate();
+ // Pick off the first limb:
+ limb_type limb;
+ limb_type mask = ~static_cast<limb_type>(0);
+ From fl;
+ eval_bitwise_and(fl, t, mask);
+ eval_convert_to(&limb, fl);
+ to = static_cast<to_type>(limb);
+ eval_right_shift(t, std::numeric_limits<limb_type>::digits);
+ //
+ // Then keep picking off more limbs until "t" is zero:
+ //
+ To l;
+ unsigned shift = std::numeric_limits<limb_type>::digits;
+ while(!eval_is_zero(t))
+ {
+ eval_bitwise_and(fl, t, mask);
+ eval_convert_to(&limb, fl);
+ l = static_cast<to_type>(limb);
+ eval_right_shift(t, std::numeric_limits<limb_type>::digits);
+ eval_ldexp(l, l, shift);
+ eval_add(to, l);
+ shift += std::numeric_limits<limb_type>::digits;
+ }
+ //
+ // Finish off by setting the sign:
+ //
+ if(is_neg)
+ to.negate();
+}
+
+template <class To, class From>
+void generic_interconvert(To& to, const From& from, const mpl::int_<number_kind_integer>& /*to_type*/, const mpl::int_<number_kind_integer>& /*from_type*/)
+{
+ using default_ops::eval_get_sign;
+ using default_ops::eval_bitwise_and;
+ using default_ops::eval_convert_to;
+ using default_ops::eval_right_shift;
+ using default_ops::eval_left_shift;
+ using default_ops::eval_bitwise_or;
+ // smallest unsigned type handled natively by "From" is likely to be it's limb_type:
+ typedef typename canonical<unsigned char, From>::type limb_type;
+ // get the corresponding type that we can assign to "To":
+ typedef typename canonical<limb_type, To>::type to_type;
+ From t(from);
+ bool is_neg = eval_get_sign(t) < 0;
+ if(is_neg)
+ t.negate();
+ // Pick off the first limb:
+ limb_type limb;
+ limb_type mask = ~static_cast<limb_type>(0);
+ From fl;
+ eval_bitwise_and(fl, t, mask);
+ eval_convert_to(&limb, fl);
+ to = static_cast<to_type>(limb);
+ eval_right_shift(t, std::numeric_limits<limb_type>::digits);
+ //
+ // Then keep picking off more limbs until "t" is zero:
+ //
+ To l;
+ unsigned shift = std::numeric_limits<limb_type>::digits;
+ while(!eval_is_zero(t))
+ {
+ eval_bitwise_and(fl, t, mask);
+ eval_convert_to(&limb, fl);
+ l = static_cast<to_type>(limb);
+ eval_right_shift(t, std::numeric_limits<limb_type>::digits);
+ eval_left_shift(l, shift);
+ eval_bitwise_or(to, l);
+ shift += std::numeric_limits<limb_type>::digits;
+ }
+ //
+ // Finish off by setting the sign:
+ //
+ if(is_neg)
+ to.negate();
+}
+
+template <class To, class From>
+void generic_interconvert(To& to, const From& from, const mpl::int_<number_kind_floating_point>& /*to_type*/, const mpl::int_<number_kind_floating_point>& /*from_type*/)
+{
+ //
+ // The code here only works when the radix of "From" is 2, we could try shifting by other
+ // radixes but it would complicate things.... use a string convertion when the radix is other
+ // than 2:
+ //
+ if(std::numeric_limits<mp_number<From> >::radix != 2)
+ {
+ to = from.str(0, std::ios_base::fmtflags()).c_str();
+ return;
+ }
+
+
+ typedef typename canonical<unsigned char, To>::type ui_type;
+
+ using default_ops::eval_fpclassify;
+ using default_ops::eval_add;
+ using default_ops::eval_subtract;
+ using default_ops::eval_convert_to;
+
+ //
+ // First classify the input, then handle the special cases:
+ //
+ int c = eval_fpclassify(from);
+
+ if(c == FP_ZERO)
+ {
+ to = ui_type(0);
+ return;
+ }
+ else if(c == FP_NAN)
+ {
+ to = "nan";
+ return;
+ }
+ else if(c == FP_INFINITE)
+ {
+ to = "inf";
+ if(eval_get_sign(from) < 0)
+ to.negate();
+ return;
+ }
+
+ typename From::exponent_type e;
+ From f, term;
+ to = ui_type(0);
+
+ eval_frexp(f, from, &e);
+
+ static const int shift = std::numeric_limits<long long>::digits - 1;
+
+ while(!eval_is_zero(f))
+ {
+ // extract int sized bits from f:
+ eval_ldexp(f, f, shift);
+ eval_floor(term, f);
+ e -= shift;
+ eval_ldexp(to, to, shift);
+ long long ll;
+ eval_convert_to(&ll, term);
+ eval_add(to, ll);
+ eval_subtract(f, term);
+ }
+ typedef typename To::exponent_type to_exponent;
+ if((e > (std::numeric_limits<to_exponent>::max)()) || (e < (std::numeric_limits<to_exponent>::min)()))
+ {
+ to = "inf";
+ if(eval_get_sign(from) < 0)
+ to.negate();
+ return;
+ }
+ eval_ldexp(to, to, static_cast<to_exponent>(e));
+}
+
+template <class To, class From>
+void generic_interconvert(To& to, const From& from, const mpl::int_<number_kind_rational>& /*to_type*/, const mpl::int_<number_kind_rational>& /*from_type*/)
+{
+ typedef typename component_type<mp_number<From> >::type from_component_type;
+ typedef typename component_type<mp_number<To> >::type to_component_type;
+
+ mp_number<From> t(from);
+ to_component_type n(numerator(t)), d(denominator(t));
+ using default_ops::assign_components;
+ assign_components(to, n.backend(), d.backend());
+}
+
+template <class To, class From>
+void generic_interconvert(To& to, const From& from, const mpl::int_<number_kind_rational>& /*to_type*/, const mpl::int_<number_kind_integer>& /*from_type*/)
+{
+ typedef typename component_type<mp_number<To> >::type to_component_type;
+
+ mp_number<From> t(from);
+ to_component_type n(t), d(1);
+ using default_ops::assign_components;
+ assign_components(to, n.backend(), d.backend());
+}
+
+template <class To, class From>
+void generic_interconvert(To& to, const From& from, const mpl::int_<number_kind_floating_point>& /*to_type*/, const mpl::int_<number_kind_rational>& /*from_type*/)
+{
+ typedef typename component_type<mp_number<From> >::type from_component_type;
+ using default_ops::eval_divide;
+
+ mp_number<From> t(from);
+ from_component_type n(numerator(t)), d(denominator(t));
+ mp_number<To> fn(n), fd(d);
+ eval_divide(to, fn.backend(), fd.backend());
+}
+
+}}} // namespaces
+
+#endif // BOOST_MP_GENERIC_INTERCONVERT_HPP
+

Modified: sandbox/big_number/boost/multiprecision/mp_number.hpp
==============================================================================
--- sandbox/big_number/boost/multiprecision/mp_number.hpp (original)
+++ sandbox/big_number/boost/multiprecision/mp_number.hpp 2012-03-29 14:00:06 EDT (Thu, 29 Mar 2012)
@@ -19,7 +19,7 @@
 #include <boost/type_traits/is_integral.hpp>
 #include <boost/type_traits/make_unsigned.hpp>
 #include <boost/throw_exception.hpp>
-#include <boost/multiprecision/detail/default_ops.hpp>
+#include <boost/multiprecision/detail/generic_interconvert.hpp>
 
 namespace boost{ namespace multiprecision{
 
@@ -63,6 +63,14 @@
    {
       m_backend = val.backend();
    }
+ template <class Other, bool ET>
+ mp_number(const mp_number<Other, ET>& val, typename disable_if<boost::is_convertible<Other, Backend> >::type* dummy1 = 0)
+ {
+ //
+ // Attempt a generic interconvertion:
+ //
+ detail::generic_interconvert(backend(), val.backend(), number_category<Backend>(), number_category<Other>());
+ }
    template <class V>
    mp_number(V v1, V v2, typename enable_if<mpl::or_<boost::is_arithmetic<V>, is_same<std::string, V>, is_convertible<V, const char*> > >::type* dummy1 = 0)
    {
@@ -125,6 +133,17 @@
       return *this;
    }
 
+ template <class Other>
+ typename disable_if<is_convertible<Other, Backend>, mp_number<Backend, ExpressionTemplates>& >::type
+ operator=(const mp_number<Other>& v)
+ {
+ //
+ // Attempt a generic interconvertion:
+ //
+ detail::generic_interconvert(backend(), v.backend(), number_category<Backend>(), number_category<Other>());
+ return *this;
+ }
+
    template <class tag, class Arg1, class Arg2, class Arg3>
    mp_number(const detail::mp_exp<tag, Arg1, Arg2, Arg3>& e)
    {

Modified: sandbox/big_number/libs/multiprecision/test/Jamfile.v2
==============================================================================
--- sandbox/big_number/libs/multiprecision/test/Jamfile.v2 (original)
+++ sandbox/big_number/libs/multiprecision/test/Jamfile.v2 2012-03-29 14:00:06 EDT (Thu, 29 Mar 2012)
@@ -626,6 +626,17 @@
          [ check-target-builds ../config//has_gmp : : <build>no ]
         : test_rational_io_mpz ;
 
+run test_generic_conv.cpp
+ : # command line
+ : # input files
+ : # requirements
+ [ check-target-builds ../config//has_gmp : <define>TEST_GMP <source>gmp : ]
+ [ check-target-builds ../config//has_tommath : <define>TEST_TOMMATH <source>$(TOMMATH) : ]
+ [ check-target-builds ../config//has_mpfr : <define>TEST_MPFR <source>mpfr : ]
+ release
+ ;
+
+
 run ../example/gmp_snips.cpp gmp
         : # command line
         : # input files

Added: sandbox/big_number/libs/multiprecision/test/test_generic_conv.cpp
==============================================================================
--- (empty file)
+++ sandbox/big_number/libs/multiprecision/test/test_generic_conv.cpp 2012-03-29 14:00:06 EDT (Thu, 29 Mar 2012)
@@ -0,0 +1,148 @@
+///////////////////////////////////////////////////////////////
+// Copyright 2012 John Maddock. Distributed under the Boost
+// Software License, Version 1.0. (See accompanying file
+// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_
+//
+
+#ifdef _MSC_VER
+# define _SCL_SECURE_NO_WARNINGS
+#endif
+
+#include <boost/detail/lightweight_test.hpp>
+#include <boost/array.hpp>
+#include "test.hpp"
+
+#include <boost/multiprecision/cpp_dec_float.hpp>
+#include <boost/multiprecision/cpp_int.hpp>
+#include <boost/multiprecision/random.hpp>
+
+#ifdef TEST_GMP
+#include <boost/multiprecision/gmp.hpp>
+#endif
+#ifdef TEST_TOMMATH
+#include <boost/multiprecision/tommath.hpp>
+#endif
+#ifdef TEST_MPFR
+#include <boost/multiprecision/mpfr.hpp>
+#endif
+
+int main()
+{
+ using namespace boost::multiprecision;
+ using namespace boost::random;
+
+ independent_bits_engine<mt11213b, 1024, cpp_int> gen;
+
+ for(unsigned i = 0; i < 100; ++i)
+ {
+ cpp_int c = gen();
+ //
+ // Integer to integer conversions first:
+ //
+#ifdef TEST_GMP
+ mpz_int z(c);
+ cpp_int t(z);
+ BOOST_CHECK_EQUAL(t, c);
+ z = -c;
+ t = -z;
+ BOOST_CHECK_EQUAL(t, c);
+#endif
+#ifdef TEST_TOMMATH
+ tom_int tom(c);
+ cpp_int t2(tom);
+ BOOST_CHECK_EQUAL(t2, c);
+ tom = -c;
+ t2 = -tom;
+ BOOST_CHECK_EQUAL(t2, c);
+#endif
+ //
+ // Now integer to float:
+ //
+ typedef mp_number<cpp_dec_float<500> > dec_float_500;
+ dec_float_500 df(c);
+ dec_float_500 df2(c.str());
+ BOOST_CHECK_EQUAL(df, df2);
+ df = -c;
+ df2 = -df2;
+ BOOST_CHECK_EQUAL(df, df2);
+#ifdef TEST_GMP
+ typedef mp_number<gmp_float<500> > mpf_type;
+ mpf_type mpf(c);
+ mpf_type mpf2(c.str());
+ BOOST_CHECK_EQUAL(mpf, mpf2);
+ mpf = -c;
+ mpf2 = -mpf2;
+ BOOST_CHECK_EQUAL(mpf, mpf2);
+#endif
+#ifdef TEST_MPFR
+ typedef mp_number<mpfr_float_backend<500> > mpfr_type;
+ mpfr_type mpfr(c);
+ mpfr_type mpfr2(c.str());
+ BOOST_CHECK_EQUAL(mpfr, mpfr2);
+ mpfr = -c;
+ mpfr2 = -mpfr2;
+ BOOST_CHECK_EQUAL(mpfr, mpfr2);
+#endif
+ //
+ // Now float to float:
+ //
+ df = c;
+ df /= dec_float_500(gen());
+ dec_float_500 tol("1e-500");
+#ifdef TEST_GMP
+ mpf = df;
+ mpf2 = df.str();
+ BOOST_CHECK_EQUAL(mpf, mpf2);
+ df = mpf;
+ df2 = mpf.str();
+ BOOST_CHECK(fabs((df - df2) / df) < tol);
+#endif
+#ifdef TEST_MPFR
+ mpfr = df;
+ mpfr2 = df.str();
+ BOOST_CHECK_EQUAL(mpfr, mpfr2);
+ df = mpfr;
+ df2 = mpfr.str();
+ BOOST_CHECK(fabs((df - df2) / df) < tol);
+#endif
+ //
+ // Rational to rational conversions:
+ //
+ cpp_rational cppr(c, gen());
+#ifdef TEST_GMP
+ mpq_rational mpq(cppr);
+ cpp_rational cppr2(mpq);
+ BOOST_CHECK_EQUAL(cppr, cppr2);
+#endif
+#ifdef TEST_TOMMATH
+ tom_rational tr(cppr);
+ cpp_rational cppr3(tr);
+ BOOST_CHECK_EQUAL(cppr, cppr3);
+#endif
+ //
+ // Integer to rational conversions:
+ //
+#ifdef TEST_GMP
+ mpq = c;
+ mpq_rational mpq2 = c.str();
+ BOOST_CHECK_EQUAL(mpq, mpq2);
+#endif
+#ifdef TEST_TOMMATH
+ tr = c;
+ tom_rational tr2 = c.str();
+ BOOST_CHECK_EQUAL(tr, tr2);
+#endif
+ //
+ // Rational to float:
+ //
+ df = cppr;
+ df2 = numerator(cppr);
+ df2 /= dec_float_500(denominator(cppr));
+ BOOST_CHECK(fabs(df - df2) / df2 < tol);
+ }
+
+ return boost::report_errors();
+}
+
+
+


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk