diff --git a/src/core/model/int64x64.cc b/src/core/model/int64x64.cc index b83c4a0fe..e64ac8c4f 100644 --- a/src/core/model/int64x64.cc +++ b/src/core/model/int64x64.cc @@ -14,34 +14,13 @@ NS_LOG_COMPONENT_DEFINE ("int64x64"); 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 @@ std::ostream &operator << (std::ostream &os, const int64x64_t &value) } } - os << hi << "."; - os.flags (ff); // Restore stream flags + os << std::right << hi << "."; - uint64_t low = value.GetLow (); - uint8_t msd = MostSignificantDigit (~((uint64_t)0)); - do + os << std::noshowpos; + + 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 @@ static uint64_t ReadDigits (std::string str) 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 @@ std::istream &operator >> (std::istream &is, int64x64_t &value) 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; diff --git a/src/core/model/int64x64.h b/src/core/model/int64x64.h index 270cae9d8..0a7f171eb 100644 --- a/src/core/model/int64x64.h +++ b/src/core/model/int64x64.h @@ -79,6 +79,18 @@ INT64X64_OP_CMP (<=) 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); diff --git a/src/core/test/int64x64-test-suite.cc b/src/core/test/int64x64-test-suite.cc index 4208bf50e..69393c0ca 100644 --- a/src/core/test/int64x64-test-suite.cc +++ b/src/core/test/int64x64-test-suite.cc @@ -1,6 +1,8 @@ #include "ns3/int64x64.h" #include "ns3/test.h" +#include + using namespace ns3; class Int64x64FracTestCase : public TestCase @@ -71,8 +73,8 @@ Int64x64InputTestCase::DoRun (void) 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 @@ Int64x64InputOutputTestCase::CheckString (std::string str) 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 @@ Int64x64Bug863TestCase::DoRun (void) 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 @@ public: 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); }