[Bug 1786] os << int64x64_t prints un-normalized fractional values

This commit is contained in:
Peter D. Barnes, Jr.
2014-01-10 17:24:06 -08:00
parent 472839ca28
commit f593f652a1
3 changed files with 160 additions and 48 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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 @@ 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);
}