$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
Subject: [Boost-commit] svn:boost r75714 - in sandbox/big_number: boost/multiprecision libs/multiprecision/test
From: john_at_[hidden]
Date: 2011-11-28 11:28:32
Author: johnmaddock
Date: 2011-11-28 11:28:31 EST (Mon, 28 Nov 2011)
New Revision: 75714
URL: http://svn.boost.org/trac/boost/changeset/75714
Log:
Fix IO of infinities and NaN's.
Add IO round trip test and adjust max_digits10 accordingly.
Text files modified: 
   sandbox/big_number/boost/multiprecision/cpp_float.hpp         |    53 +++++++++++++++++++--                   
   sandbox/big_number/boost/multiprecision/gmp.hpp               |     4                                         
   sandbox/big_number/boost/multiprecision/mpfr.hpp              |    17 ++++++                                  
   sandbox/big_number/libs/multiprecision/test/test.hpp          |     2                                         
   sandbox/big_number/libs/multiprecision/test/test_float_io.cpp |    98 +++++++++++++++++++++++++++++++++++++++ 
   5 files changed, 163 insertions(+), 11 deletions(-)
Modified: sandbox/big_number/boost/multiprecision/cpp_float.hpp
==============================================================================
--- sandbox/big_number/boost/multiprecision/cpp_float.hpp	(original)
+++ sandbox/big_number/boost/multiprecision/cpp_float.hpp	2011-11-28 11:28:31 EST (Mon, 28 Nov 2011)
@@ -53,6 +53,11 @@
    static const boost::int32_t mp_elem_number       = static_cast<boost::int32_t>(cpp_float_digits10_num_base + 2);
    static const boost::int32_t mp_elem_mask = static_cast<boost::int32_t>(100000000);
 
+public:
+   static const boost::int32_t cpp_float_total_digits10 = mp_elem_number * mp_elem_digits10;
+
+private:
+
    typedef enum enum_fpclass
    {
       mp_finite,
@@ -1641,6 +1646,20 @@
 template <unsigned Digits10>
 std::string cpp_float<Digits10>::str(std::streamsize number_of_digits, std::ios_base::fmtflags f) const
 {
+   if(this->isinf())
+   {
+      if(this->isneg())
+         return "-inf";
+      else if(f & std::ios_base::showpos)
+         return "+inf";
+      else 
+         return "inf";
+   }
+   else if(this->isnan())
+   {
+      return "nan";
+   }
+
    std::string str;
    std::streamsize org_digits(number_of_digits);
    boost::int64_t my_exp = order();
@@ -1799,17 +1818,39 @@
    // Get a possible +/- sign and remove it.
    neg = false;
 
-   if((pos = str.find(static_cast<char>('-'))) != std::string::npos)
+   if(str.size())
    {
-      neg = true;
-      str.erase(pos, static_cast<std::size_t>(1u));
+      if(str[0] == '-')
+      {
+         neg = true;
+         str.erase(0, 1);
+      }
+      else if(str[0] == '+')
+      {
+         str.erase(0, 1);
+      }
    }
-
-   if((pos = str.find(static_cast<char>('+'))) != std::string::npos)
+   //
+   // Special cases for infinities and NaN's:
+   //
+   if((str == "inf") || (str == "INF") || (str == "infinity") || (str == "INFINITY"))
    {
-      str.erase(pos, static_cast<std::size_t>(1u));
+      if(neg)
+      {
+         *this = this->inf();
+         this->negate();
+      }
+      else
+         *this = this->inf();
+      return true;
+   }
+   if((str.size() >= 3) && ((str.substr(0, 3) == "nan") || (str.substr(0, 3) == "NAN")))
+   {
+      *this = this->nan();
+      return true;
    }
 
+
    // Remove leading zeros for all input types.
    const std::string::iterator fwd_it_leading_zero = std::find_if(str.begin(), str.end(), char_is_nonzero_predicate);
 
Modified: sandbox/big_number/boost/multiprecision/gmp.hpp
==============================================================================
--- sandbox/big_number/boost/multiprecision/gmp.hpp	(original)
+++ sandbox/big_number/boost/multiprecision/gmp.hpp	2011-11-28 11:28:31 EST (Mon, 28 Nov 2011)
@@ -1788,8 +1788,8 @@
    }
    BOOST_STATIC_CONSTEXPR int digits = static_cast<int>(((Digits10 + 1) * 1000L) / 301L);
    BOOST_STATIC_CONSTEXPR int digits10 = Digits10;
-   // Is this really correct???
-   BOOST_STATIC_CONSTEXPR int max_digits10 = Digits10 + 1;
+   // Have to allow for a possible extra limb inside the gmp data structure:
+   BOOST_STATIC_CONSTEXPR int max_digits10 = Digits10 + 2 + ((GMP_LIMB_BITS * 301L) / 1000L);
    BOOST_STATIC_CONSTEXPR bool is_signed = true;
    BOOST_STATIC_CONSTEXPR bool is_integer = false;
    BOOST_STATIC_CONSTEXPR bool is_exact = false;
Modified: sandbox/big_number/boost/multiprecision/mpfr.hpp
==============================================================================
--- sandbox/big_number/boost/multiprecision/mpfr.hpp	(original)
+++ sandbox/big_number/boost/multiprecision/mpfr.hpp	2011-11-28 11:28:31 EST (Mon, 28 Nov 2011)
@@ -168,6 +168,21 @@
 
       std::string result;
       mp_exp_t e;
+      if(mpfr_inf_p(m_data))
+      {
+         if(mpfr_sgn(m_data) < 0)
+            result = "-inf";
+         else if(f & std::ios_base::showpos)
+            result = "+inf";
+         else
+            result = "inf";
+         return result;
+      }
+      if(mpfr_nan_p(m_data))
+      {
+         result = "nan";
+         return result;
+      }
       if(mpfr_zero_p(m_data))
       {
          e = 0;
@@ -1059,7 +1074,7 @@
    BOOST_STATIC_CONSTEXPR int digits = static_cast<int>(((Digits10 + 1) * 1000L) / 301L);
    BOOST_STATIC_CONSTEXPR int digits10 = Digits10;
    // Is this really correct???
-   BOOST_STATIC_CONSTEXPR int max_digits10 = Digits10 + 1;
+   BOOST_STATIC_CONSTEXPR int max_digits10 = Digits10 + 2;
    BOOST_STATIC_CONSTEXPR bool is_signed = true;
    BOOST_STATIC_CONSTEXPR bool is_integer = false;
    BOOST_STATIC_CONSTEXPR bool is_exact = false;
Modified: sandbox/big_number/libs/multiprecision/test/test.hpp
==============================================================================
--- sandbox/big_number/libs/multiprecision/test/test.hpp	(original)
+++ sandbox/big_number/libs/multiprecision/test/test.hpp	2011-11-28 11:28:31 EST (Mon, 28 Nov 2011)
@@ -63,7 +63,7 @@
 template <class T>
 inline int digits_of(const T&)
 {
-   return std::numeric_limits<T>::is_specialized ? std::numeric_limits<T>::digits10 + 2 : std::numeric_limits<long double>::digits10 + 2;
+   return std::numeric_limits<T>::is_specialized ? std::numeric_limits<T>::max_digits10 + 2 : std::numeric_limits<long double>::max_digits10 + 2;
 }
 
 inline std::ostream& report_where(const char* file, int line, const char* function)
Modified: sandbox/big_number/libs/multiprecision/test/test_float_io.cpp
==============================================================================
--- sandbox/big_number/libs/multiprecision/test/test_float_io.cpp	(original)
+++ sandbox/big_number/libs/multiprecision/test/test_float_io.cpp	2011-11-28 11:28:31 EST (Mon, 28 Nov 2011)
@@ -33,7 +33,9 @@
 #include <boost/multiprecision/cpp_float.hpp>
 #endif
 
-#include <boost/detail/lightweight_test.hpp>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include "test.hpp"
 #include <boost/array.hpp>
 #include <iostream>
 #include <iomanip>
@@ -160,6 +162,85 @@
          }
       }
    }
+
+   if(std::numeric_limits<mp_t>::has_infinity)
+   {
+      T val = std::numeric_limits<T>::infinity();
+      BOOST_CHECK_EQUAL(val.str(), "inf");
+      BOOST_CHECK_EQUAL(val.str(0, std::ios_base::showpos), "+inf");
+      val = -val;
+      BOOST_CHECK_EQUAL(val.str(), "-inf");
+      BOOST_CHECK_EQUAL(val.str(0, std::ios_base::showpos), "-inf");
+
+      val = "inf";
+      BOOST_CHECK_EQUAL(val, std::numeric_limits<T>::infinity());
+      val = "+inf";
+      BOOST_CHECK_EQUAL(val, std::numeric_limits<T>::infinity());
+      val = "-inf";
+      BOOST_CHECK_EQUAL(val, -std::numeric_limits<T>::infinity());
+   }
+   if(std::numeric_limits<mp_t>::has_quiet_NaN)
+   {
+      T val = std::numeric_limits<T>::quiet_NaN();
+      BOOST_CHECK_EQUAL(val.str(), "nan");
+      val = "nan";
+      BOOST_CHECK(boost::math::isnan(val));
+   }
+}
+
+template <class T>
+T generate_random()
+{
+   static boost::random::mt19937 gen;
+   T val = gen();
+   T prev_val = -1;
+   while(val != prev_val)
+   {
+      val *= (gen.max)();
+      prev_val = val;
+      val += gen();
+   }
+   int e;
+   val = frexp(val, &e);
+
+   typedef typename T::backend_type::exponent_type e_type;
+   static boost::random::uniform_int_distribution<e_type> ui(0, std::numeric_limits<T>::max_exponent - 10);
+   return ldexp(val, ui(gen));
+}
+
+template <class T>
+void do_round_trip(const T& val, std::ios_base::fmtflags f)
+{
+   std::stringstream ss;
+   ss << std::setprecision(std::numeric_limits<T>::max_digits10);
+   ss.flags(f);
+   ss << val;
+   T new_val = ss.str();
+   BOOST_CHECK_EQUAL(new_val, val);
+   new_val = val.str(0, f);
+   BOOST_CHECK_EQUAL(new_val, val);
+}
+
+template <class T>
+void do_round_trip(const T& val)
+{
+   do_round_trip(val, std::ios_base::fmtflags(0));
+   do_round_trip(val, std::ios_base::fmtflags(std::ios_base::scientific));
+   if((fabs(val) > 1) && (fabs(val) < 1e100))
+      do_round_trip(val, std::ios_base::fmtflags(std::ios_base::fixed));
+}
+
+template <class T>
+void test_round_trip()
+{
+   for(unsigned i = 0; i < 1000; ++i)
+   {
+      T val = generate_random<T>();
+      do_round_trip(val);
+      do_round_trip(T(-val));
+      do_round_trip(T(1/val));
+      do_round_trip(T(-1/val));
+   }
 }
 
 int main()
@@ -167,14 +248,29 @@
 #ifdef TEST_MPFR_50
    test<boost::multiprecision::mpfr_float_50>();
    test<boost::multiprecision::mpfr_float_100>();
+
+   test_round_trip<boost::multiprecision::mpfr_float_50>();
+   test_round_trip<boost::multiprecision::mpfr_float_100>();
 #endif
 #ifdef TEST_CPP_FLOAT
    test<boost::multiprecision::cpp_float_50>();
    test<boost::multiprecision::cpp_float_100>();
+
+   /*
+   // cpp_float has extra guard digits that messes this up:
+   test_round_trip<boost::multiprecision::cpp_float_50>();
+   test_round_trip<boost::multiprecision::cpp_float_100>();
+   */
 #endif
 #ifdef TEST_MPF_50
    test<boost::multiprecision::mpf_float_50>();
    test<boost::multiprecision::mpf_float_100>();
+   /*
+   // I can't get this to work with mpf_t - mpf_str appears
+   // not to actually print enough decimal digits:
+   test_round_trip<boost::multiprecision::mpf_float_50>();
+   test_round_trip<boost::multiprecision::mpf_float_100>();
+   */
 #endif
    return boost::report_errors();
 }