364 lines
8.9 KiB
C++
364 lines
8.9 KiB
C++
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
/*
|
|
* Copyright (c) 2006 INRIA
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation;
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
|
|
*/
|
|
#include "high-precision.h"
|
|
|
|
#include <cmath>
|
|
#include "ns3/assert.h"
|
|
|
|
|
|
namespace ns3 {
|
|
|
|
HighPrecision Abs (HighPrecision const &value)
|
|
{
|
|
if (value.Compare (HighPrecision::Zero ()) <= 0)
|
|
{
|
|
HighPrecision v = HighPrecision::Zero ();
|
|
v.Sub (value);
|
|
return v;
|
|
}
|
|
else
|
|
{
|
|
return value;
|
|
}
|
|
}
|
|
|
|
} /* namespace ns3 */
|
|
|
|
#include "ns3/test.h"
|
|
|
|
#define CHECK_EXPECTED(a,b) \
|
|
NS_TEST_ASSERT_MSG_EQ (a.GetInteger (),b,"Arithmetic failure: " << (a.GetInteger ()) << "!=" << (b))
|
|
|
|
#define V(v) \
|
|
HighPrecision (v, false)
|
|
|
|
namespace ns3 {
|
|
|
|
class Hp128ArithmeticTestCase : public TestCase
|
|
{
|
|
public:
|
|
Hp128ArithmeticTestCase ();
|
|
virtual bool DoRun (void);
|
|
};
|
|
|
|
Hp128ArithmeticTestCase::Hp128ArithmeticTestCase ()
|
|
: TestCase ("Check basic arithmetic operations")
|
|
{
|
|
}
|
|
bool
|
|
Hp128ArithmeticTestCase::DoRun (void)
|
|
{
|
|
HighPrecision a, b;
|
|
a = HighPrecision (1, false);
|
|
b = HighPrecision (1, false);
|
|
|
|
a.Sub (b);
|
|
CHECK_EXPECTED (a, 0);
|
|
|
|
a = V (1);
|
|
a.Sub (V (2));
|
|
CHECK_EXPECTED (a, -1);
|
|
|
|
a = V (1);
|
|
a.Sub (V (3));
|
|
CHECK_EXPECTED (a, -2);
|
|
|
|
a = V (1);
|
|
a.Sub (V (-1));
|
|
CHECK_EXPECTED (a, 2);
|
|
|
|
a = V (1);
|
|
a.Sub (V (-2));
|
|
CHECK_EXPECTED (a, 3);
|
|
|
|
a = V (-3);
|
|
a.Sub (V (-4));
|
|
CHECK_EXPECTED (a, 1);
|
|
|
|
a = V (-2);
|
|
a.Sub (V (3));
|
|
CHECK_EXPECTED (a, -5);
|
|
|
|
a = V (1);
|
|
a.Add (V (2));
|
|
CHECK_EXPECTED (a, 3);
|
|
|
|
a = V (1);
|
|
a.Add (V (-3));
|
|
CHECK_EXPECTED (a, -2);
|
|
|
|
a = V (0);
|
|
a.Add (V (0));
|
|
CHECK_EXPECTED (a, 0);
|
|
|
|
a = V (0);
|
|
a.Mul (V (0));
|
|
CHECK_EXPECTED (a, 0);
|
|
a = V (0);
|
|
a.Mul (V (1));
|
|
CHECK_EXPECTED (a, 0);
|
|
a = V (0);
|
|
a.Mul (V (-1));
|
|
CHECK_EXPECTED (a, 0);
|
|
a = V (1);
|
|
a.Mul (V (0));
|
|
CHECK_EXPECTED (a, 0);
|
|
a = V (1);
|
|
a.Mul (V (1));
|
|
CHECK_EXPECTED (a, 1);
|
|
a = V (1);
|
|
a.Mul (V (-1));
|
|
CHECK_EXPECTED (a, -1);
|
|
a = V (-1);
|
|
a.Mul (V (-1));
|
|
CHECK_EXPECTED (a, 1);
|
|
|
|
a = V (0);
|
|
a.Mul (V (1));
|
|
CHECK_EXPECTED (a, 0);
|
|
a = V (0);
|
|
a.Mul (V (-1));
|
|
CHECK_EXPECTED (a, 0);
|
|
a = V (1);
|
|
a.Mul (V (1));
|
|
CHECK_EXPECTED (a, 1);
|
|
a = V (1);
|
|
a.Mul (V (-1));
|
|
CHECK_EXPECTED (a, -1);
|
|
a = V (-1);
|
|
a.Mul (V (1));
|
|
CHECK_EXPECTED (a, -1);
|
|
a = V (-1);
|
|
a.Mul (V (-1));
|
|
CHECK_EXPECTED (a, 1);
|
|
|
|
|
|
|
|
a = V (2);
|
|
a.Mul (V (3));
|
|
a.Div (V (3));
|
|
CHECK_EXPECTED (a, 2);
|
|
|
|
// Below, the division loses precision because 2/3 is not
|
|
// representable exactly in 64.64 integers. So, we got
|
|
// something super close but the final rounding kills us.
|
|
a = V (2);
|
|
a.Div (V (3));
|
|
a.Mul (V (3));
|
|
CHECK_EXPECTED (a, 1);
|
|
|
|
// The example below shows that we really do not lose
|
|
// much precision internally: it is almost always the
|
|
// final conversion which loses precision.
|
|
a = V (2000000000);
|
|
a.Div (V (3));
|
|
a.Mul (V (3));
|
|
CHECK_EXPECTED (a, 1999999999);
|
|
|
|
return false;
|
|
}
|
|
|
|
class Hp128Bug455TestCase : public TestCase
|
|
{
|
|
public:
|
|
Hp128Bug455TestCase ();
|
|
virtual bool DoRun (void);
|
|
};
|
|
|
|
Hp128Bug455TestCase::Hp128Bug455TestCase ()
|
|
: TestCase ("Test case for bug 455")
|
|
{
|
|
}
|
|
bool
|
|
Hp128Bug455TestCase::DoRun (void)
|
|
{
|
|
HighPrecision a = HighPrecision (0.1);
|
|
a.Div (HighPrecision (1.25));
|
|
NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 0.08, "The original testcase");
|
|
a = HighPrecision (0.5);
|
|
a.Mul (HighPrecision (5));
|
|
NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 2.5, "Simple test for multiplication");
|
|
a = HighPrecision (-0.5);
|
|
a.Mul (HighPrecision (5));
|
|
NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), -2.5, "Test sign, first operation negative");
|
|
a = HighPrecision (-0.5);
|
|
a.Mul (HighPrecision (-5));
|
|
NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 2.5, "both operands negative");
|
|
a = HighPrecision (0.5);
|
|
a.Mul (HighPrecision (-5));
|
|
NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), -2.5, "only second operand negative");
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
class Hp128Bug863TestCase : public TestCase
|
|
{
|
|
public:
|
|
Hp128Bug863TestCase ();
|
|
virtual bool DoRun (void);
|
|
};
|
|
|
|
Hp128Bug863TestCase::Hp128Bug863TestCase ()
|
|
: TestCase ("Test case for bug 863")
|
|
{
|
|
}
|
|
bool
|
|
Hp128Bug863TestCase::DoRun (void)
|
|
{
|
|
HighPrecision a = HighPrecision (0.9);
|
|
a.Div (HighPrecision (1));
|
|
NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 0.9, "The original testcase");
|
|
a = HighPrecision (0.5);
|
|
a.Div (HighPrecision (0.5));
|
|
NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 1.0, "Simple test for division");
|
|
a = HighPrecision (-0.5);
|
|
NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), -0.5, "Check that we actually convert doubles correctly");
|
|
a.Div (HighPrecision (0.5));
|
|
NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), -1.0, "first argument negative");
|
|
a = HighPrecision (0.5);
|
|
a.Div (HighPrecision (-0.5));
|
|
NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), -1.0, "second argument negative");
|
|
a = HighPrecision (-0.5);
|
|
a.Div (HighPrecision (-0.5));
|
|
NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 1.0, "both arguments negative");
|
|
|
|
return false;
|
|
}
|
|
|
|
class Hp128CompareTestCase : public TestCase
|
|
{
|
|
public:
|
|
Hp128CompareTestCase ();
|
|
virtual bool DoRun (void);
|
|
};
|
|
|
|
Hp128CompareTestCase::Hp128CompareTestCase ()
|
|
: TestCase ("Check basic compare operations")
|
|
{
|
|
}
|
|
bool
|
|
Hp128CompareTestCase::DoRun (void)
|
|
{
|
|
HighPrecision a, b;
|
|
|
|
a = V (-1);
|
|
b = V (1);
|
|
NS_TEST_ASSERT_MSG_EQ (a.Compare (b), -1, "a is smaller than b");
|
|
a = V (-1);
|
|
b = V (-2);
|
|
NS_TEST_ASSERT_MSG_EQ (a.Compare (b), 1, "a is bigger than b");
|
|
a = V (-1);
|
|
b = V (-1);
|
|
NS_TEST_ASSERT_MSG_EQ (a.Compare (b), 0, "a is equal to b");
|
|
|
|
a = V (1);
|
|
b = V (-1);
|
|
NS_TEST_ASSERT_MSG_EQ (a.Compare (b), 1, "a is bigger than b");
|
|
a = V (1);
|
|
b = V (2);
|
|
NS_TEST_ASSERT_MSG_EQ (a.Compare (b), -1, "a is smaller than b");
|
|
a = V (1);
|
|
b = V (1);
|
|
NS_TEST_ASSERT_MSG_EQ (a.Compare (b), 0, "a is equal to b");
|
|
|
|
return false;
|
|
}
|
|
|
|
class Hp128InvertTestCase : public TestCase
|
|
{
|
|
public:
|
|
Hp128InvertTestCase ();
|
|
virtual bool DoRun (void);
|
|
};
|
|
|
|
Hp128InvertTestCase::Hp128InvertTestCase ()
|
|
: TestCase ("Test case for invertion")
|
|
{
|
|
}
|
|
|
|
bool
|
|
Hp128InvertTestCase::DoRun (void)
|
|
{
|
|
#define TEST(factor) \
|
|
do { \
|
|
HighPrecision a; \
|
|
a = HighPrecision::Invert (factor); \
|
|
HighPrecision b = V (factor); \
|
|
b.MulByInvert (a); \
|
|
NS_TEST_ASSERT_MSG_EQ (b.GetInteger (), 1, \
|
|
"x * 1/x should be 1 for x=" << factor); \
|
|
HighPrecision c = V (1); \
|
|
c.MulByInvert (a); \
|
|
NS_TEST_ASSERT_MSG_EQ (c.GetInteger (), 0, \
|
|
"1 * 1/x should be 0 for x=" << factor); \
|
|
HighPrecision d = V (1); \
|
|
d.Div (V(factor)); \
|
|
NS_TEST_ASSERT_MSG_EQ (d.GetDouble (), c.GetDouble (), \
|
|
"1 * 1/x should be equal to 1/x for x=" << factor); \
|
|
HighPrecision e = V (-factor); \
|
|
e.MulByInvert (a); \
|
|
NS_TEST_ASSERT_MSG_EQ (e.GetInteger (), -1, \
|
|
"-x * 1/x should be -1 for x=" << factor); \
|
|
} while(false)
|
|
|
|
TEST(2);
|
|
TEST(3);
|
|
TEST(4);
|
|
TEST(5);
|
|
TEST(6);
|
|
TEST(10);
|
|
TEST(99);
|
|
TEST(100);
|
|
TEST(1000);
|
|
TEST(10000);
|
|
TEST(100000);
|
|
TEST(100000);
|
|
TEST(1000000);
|
|
TEST(10000000);
|
|
TEST(100000000);
|
|
TEST(1000000000);
|
|
TEST(10000000000LL);
|
|
TEST(100000000000LL);
|
|
TEST(1000000000000LL);
|
|
TEST(10000000000000LL);
|
|
TEST(100000000000000LL);
|
|
TEST(1000000000000000LL);
|
|
#undef TEST
|
|
return false;
|
|
}
|
|
|
|
|
|
static class HighPrecision128TestSuite : public TestSuite
|
|
{
|
|
public:
|
|
HighPrecision128TestSuite ()
|
|
: TestSuite ("high-precision-128", UNIT)
|
|
{
|
|
AddTestCase (new Hp128ArithmeticTestCase ());
|
|
AddTestCase (new Hp128Bug455TestCase ());
|
|
AddTestCase (new Hp128Bug863TestCase ());
|
|
AddTestCase (new Hp128CompareTestCase ());
|
|
AddTestCase (new Hp128InvertTestCase ());
|
|
}
|
|
} g_highPrecision128TestSuite;
|
|
|
|
} // namespace ns3
|