[Bug 1786] os << int64x64_t prints un-normalized fractional values
authorPeter D. Barnes, Jr. <barnes26@llnl.gov>
Fri, 10 Jan 2014 17:24:06 -0800
changeset 10557 e08d256cdffc
parent 10556 8d48368f20c9
child 10558 022157dbf3e1
[Bug 1786] os << int64x64_t prints un-normalized fractional values
src/core/model/int64x64.cc
src/core/model/int64x64.h
src/core/test/int64x64-test-suite.cc
--- a/src/core/model/int64x64.cc	Fri Jan 10 17:22:45 2014 -0800
+++ b/src/core/model/int64x64.cc	Fri Jan 10 17:24:06 2014 -0800
@@ -14,34 +14,13 @@
 
 namespace ns3 {
 
-static uint8_t MostSignificantDigit (uint64_t value)
-{
-  uint8_t n = 0;
-  do
-    {
-      n++;
-      value /= 10;
-    } while (value != 0);
-  return n;
-}
-
-static uint64_t PowerOfTen (uint8_t n)
-{
-  uint64_t retval = 1;
-  while (n > 0)
-    {
-      retval *= 10;
-      n--;
-    }
-  return retval;
-}
-
 std::ostream &operator << (std::ostream &os, const int64x64_t &value)
 {
   int64_t hi = value.GetHigh ();
 
   // Save stream format flags
   std::ios_base::fmtflags ff = os.flags ();
+  os << std::setw (1);
 
   { /// \internal
     /// See \bugid{1737}:  gcc libstc++ 4.2 bug
@@ -55,24 +34,41 @@
       }
   }
   
-  os << hi << ".";
-  os.flags (ff);  // Restore stream flags
+  os << std::right << hi << ".";
+
+  os << std::noshowpos;
 
-  uint64_t low = value.GetLow ();
-  uint8_t msd = MostSignificantDigit (~((uint64_t)0));
-  do
+  int64x64_t low(0, value.GetLow ());
+  int places = 0;    // Number of decimal places printed so far
+  const bool floatfield = os.flags () & std::ios_base::floatfield;
+  bool more = true;  // Should we print more digits?
+
+  do 
     {
-      msd--;
-      uint64_t pow = PowerOfTen (msd);
-      uint8_t digit = low / pow;
-      NS_ASSERT (digit < 10);
-      os << (uint16_t) digit;
-      low -= digit * pow;
-    } while (msd > 0 && low > 0);
+      low = 10 * low;
+      int64_t digit = low.GetHigh ();
+      low -= digit;
+
+      os << std::setw (1) << digit;
+
+      ++places;
+      if (floatfield)
+	{
+	  more = places < os.precision ();
+	}
+      else  // default
+	{
+	  // Full resolution is 20 decimal digits
+	  more = low.GetLow () && (places < 20);
+	}
+
+    } while (more);
+
+  os.flags (ff);  // Restore stream flags
   return os;
 }
 
-static uint64_t ReadDigits (std::string str)
+static uint64_t ReadHiDigits (std::string str)
 {
   const char *buf = str.c_str ();
   uint64_t retval = 0;
@@ -85,6 +81,22 @@
   return retval;
 }
 
+static uint64_t ReadLoDigits (std::string str)
+{
+  int64x64_t low (0, 0);
+  const int64x64_t round (0, 5);
+
+  for (std::string::const_reverse_iterator rchar = str.rbegin ();
+       rchar != str.rend ();
+       ++rchar)
+    {
+      int digit = *rchar - '0';
+      low = (low + digit + round) / 10; 
+    }
+  
+  return low.GetLow ();
+}
+    
 std::istream &operator >> (std::istream &is, int64x64_t &value)
 {
   std::string str;
@@ -121,12 +133,12 @@
   next = str.find (".", cur);
   if (next != std::string::npos)
     {
-      hi = ReadDigits (str.substr (cur, next-cur));
-      lo = ReadDigits (str.substr (next+1, str.size ()-(next+1)));
+      hi = ReadHiDigits (str.substr (cur, next-cur));
+      lo = ReadLoDigits (str.substr (next+1, str.size ()-(next+1)));
     }
   else
     {
-      hi = ReadDigits (str.substr (cur, str.size ()-cur));
+      hi = ReadHiDigits (str.substr (cur, str.size ()-cur));
       lo = 0;
     }
   hi = negative ? -hi : hi;
--- a/src/core/model/int64x64.h	Fri Jan 10 17:22:45 2014 -0800
+++ b/src/core/model/int64x64.h	Fri Jan 10 17:24:06 2014 -0800
@@ -79,6 +79,18 @@
 INT64X64_OP_CMP (>)
 INT64X64_OP_CMP (>=)
 
+/**
+ * Output streamer for int64x64_t
+ *
+ * Values are printed with the following format flags
+ * independent of the the stream flags):
+ *   - `showpos`
+ *   - `left`
+ * The stream `width` is ignored.  If `floatfield` is set,
+ * `precision` decimal places are printed.  If `floatfield` is not set,
+ * all digits of the fractional part are printed, up to the
+ * representation limit of 20 digits; trailing zeros are omitted.
+ */
 std::ostream &operator << (std::ostream &os, const int64x64_t &val);
 std::istream &operator >> (std::istream &is, int64x64_t &val);
 
--- a/src/core/test/int64x64-test-suite.cc	Fri Jan 10 17:22:45 2014 -0800
+++ b/src/core/test/int64x64-test-suite.cc	Fri Jan 10 17:24:06 2014 -0800
@@ -1,6 +1,8 @@
 #include "ns3/int64x64.h"
 #include "ns3/test.h"
 
+#include <iomanip>
+
 using namespace ns3;
 
 class Int64x64FracTestCase : public TestCase
@@ -71,8 +73,8 @@
   CheckString ("-1.0", -1, 0);
   CheckString ("-1.0000", -1, 0);
   CheckString ("1.0000000", 1, 0);
-  CheckString ("1.08446744073709551615", 1, 8446744073709551615LL);
-  CheckString ("-1.08446744073709551615", -1, 8446744073709551615LL);
+  CheckString (" 1.000000000000000000054",  1, 1);
+  CheckString ("-1.000000000000000000054", -1, 1);
 }
 
 class Int64x64InputOutputTestCase : public TestCase
@@ -94,19 +96,20 @@
   int64x64_t value;
   iss >> value;
   std::ostringstream oss;
-  oss << value;
+  oss << std::scientific << std::setprecision (21) << value;
   NS_TEST_EXPECT_MSG_EQ (oss.str (), str, "Converted string does not match expected string");
 }
 void
 Int64x64InputOutputTestCase::DoRun (void)
 {
-  CheckString ("+1.0");
-  CheckString ("-1.0");
-  CheckString ("+20.0");
-  CheckString ("+1.08446744073709551615");
-  CheckString ("-1.08446744073709551615");
-  CheckString ("+1.18446744073709551615");
-  CheckString ("-1.18446744073709551615");
+  CheckString ("+1.000000000000000000000");
+  CheckString ("+0.000000000000000000000");
+  CheckString ("-1.000000000000000000000");
+  CheckString ("+20.000000000000000000000");
+  CheckString ("+1.084467440737095516158");
+  CheckString ("-1.084467440737095516158");
+  CheckString ("+1.184467440737095516179");
+  CheckString ("-1.184467440737095516179");
 }
 
 #define CHECK_EXPECTED(a,b) \
@@ -239,6 +242,90 @@
   NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 1.0, "both arguments negative");
 }
 
+/**
+ * See \bugid{1786}
+ */
+class Int64x64Bug1786TestCase : public TestCase
+{
+public:
+  Int64x64Bug1786TestCase ();
+  void Check (const uint64_t low, const std::string & value);
+  virtual void DoRun (void);
+};
+
+Int64x64Bug1786TestCase::Int64x64Bug1786TestCase ()
+  : TestCase ("Test case for bug 1786")
+{
+}
+void
+Int64x64Bug1786TestCase::Check (const uint64_t low, const std::string & str)
+{
+  int64x64_t value (0, low);
+  std::ostringstream oss;
+  oss << std::scientific << std::setprecision (21) << value;
+  std::cout << "    0x" << std::hex << std::setw (16) << low << std::dec
+	    << " = "    << oss.str ()
+	    << std::endl;
+  NS_TEST_EXPECT_MSG_EQ (oss.str (), str, "Fraction string not correct");
+}
+void
+Int64x64Bug1786TestCase::DoRun (void)
+{
+  std::cout << std::endl;
+  std::cout << GetName () << ":" << std::endl;
+
+  Check (                 1ULL, "+0.000000000000000000054");
+  Check (                 2ULL, "+0.000000000000000000108");
+  Check (                 3ULL, "+0.000000000000000000162");
+  Check (                 4ULL, "+0.000000000000000000216");
+  Check (                 5ULL, "+0.000000000000000000271");
+  Check (                 6ULL, "+0.000000000000000000325");
+  Check (                 7ULL, "+0.000000000000000000379");
+  Check (                 8ULL, "+0.000000000000000000433");
+  Check (                 9ULL, "+0.000000000000000000487");
+  Check (               0xAULL, "+0.000000000000000000542");
+  Check (               0xFULL, "+0.000000000000000000813");
+  Check (              0xF0ULL, "+0.000000000000000013010");
+  Check (             0xF00ULL, "+0.000000000000000208166");
+  Check (            0xF000ULL, "+0.000000000000003330669");
+  Check (           0xF0000ULL, "+0.000000000000053290705");
+  Check (          0xF00000ULL, "+0.000000000000852651282");
+  Check (         0xF000000ULL, "+0.000000000013642420526");
+  Check (        0xF0000000ULL, "+0.000000000218278728425");
+  Check (       0xF00000000ULL, "+0.000000003492459654808");
+  Check (      0xF000000000ULL, "+0.000000055879354476928");
+  Check (     0xF0000000000ULL, "+0.000000894069671630859");
+  Check (    0xF00000000000ULL, "+0.000014305114746093750");
+  Check (   0xF000000000000ULL, "+0.000228881835937500000");
+  Check (  0xF0000000000000ULL, "+0.003662109375000000000");
+  std::cout << std::endl;
+  Check (0x7FFFFFFFFFFFFFFDULL, "+0.499999999999999999837");
+  Check (0x7FFFFFFFFFFFFFFEULL, "+0.499999999999999999891");
+  Check (0x7FFFFFFFFFFFFFFFULL, "+0.499999999999999999945");
+  Check (0x8000000000000000ULL, "+0.500000000000000000000");
+  Check (0x8000000000000001ULL, "+0.500000000000000000054");
+  Check (0x8000000000000002ULL, "+0.500000000000000000108");
+  Check (0x8000000000000003ULL, "+0.500000000000000000162");
+  std::cout << std::endl;
+  Check (0xF000000000000000ULL, "+0.937500000000000000000");
+  Check (0xFF00000000000000ULL, "+0.996093750000000000000");
+  Check (0xFFF0000000000000ULL, "+0.999755859375000000000");
+  Check (0xFFFF000000000000ULL, "+0.999984741210937500000");
+  Check (0xFFFFF00000000000ULL, "+0.999999046325683593750");
+  Check (0xFFFFFF0000000000ULL, "+0.999999940395355224609");
+  Check (0xFFFFFFF000000000ULL, "+0.999999996274709701538");
+  Check (0xFFFFFFFF00000000ULL, "+0.999999999767169356346");
+  Check (0xFFFFFFFFF0000000ULL, "+0.999999999985448084771");
+  Check (0xFFFFFFFFFF000000ULL, "+0.999999999999090505298");
+  Check (0xFFFFFFFFFFF00000ULL, "+0.999999999999943156581");
+  Check (0xFFFFFFFFFFFF0000ULL, "+0.999999999999996447286");
+  Check (0xFFFFFFFFFFFFF000ULL, "+0.999999999999999777955");
+  Check (0xFFFFFFFFFFFFFF00ULL, "+0.999999999999999986122");
+  Check (0xFFFFFFFFFFFFFFF0ULL, "+0.999999999999999999132");
+  Check (0xFFFFFFFFFFFFFFFFULL, "+0.999999999999999999945");
+  
+}
+
 class Int64x64CompareTestCase : public TestCase
 {
 public:
@@ -337,6 +424,7 @@
     AddTestCase (new Int64x64ArithmeticTestCase (), TestCase::QUICK);
     AddTestCase (new Int64x64Bug455TestCase (), TestCase::QUICK);
     AddTestCase (new Int64x64Bug863TestCase (), TestCase::QUICK);
+    AddTestCase (new Int64x64Bug1786TestCase (), TestCase::QUICK);
     AddTestCase (new Int64x64CompareTestCase (), TestCase::QUICK);
     AddTestCase (new Int64x64InvertTestCase (), TestCase::QUICK);
   }