From eb432c90a5ef15dfb7309e73342a72e4a135be02 Mon Sep 17 00:00:00 2001 From: "Peter D. Barnes, Jr" Date: Mon, 9 Nov 2020 18:50:04 -0800 Subject: [PATCH] core: int64x64_t GetInt() and Round(), with test cases Merge of !473 --- src/core/model/int64x64-128.h | 31 ++++++++++++++ src/core/model/int64x64-cairo.h | 31 ++++++++++++++ src/core/model/int64x64-double.h | 23 ++++++++++ src/core/test/int64x64-test-suite.cc | 63 ++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+) diff --git a/src/core/model/int64x64-128.h b/src/core/model/int64x64-128.h index 915f8b70e..788f79d61 100644 --- a/src/core/model/int64x64-128.h +++ b/src/core/model/int64x64-128.h @@ -235,6 +235,37 @@ public: return retval; } + /** + * Truncate to an integer. + * Truncation is always toward zero, + * \return The value truncated toward zero. + */ + int64_t GetInt (void) const + { + const bool negative = _v < 0; + const uint128_t value = negative ? -_v : _v; + int64_t retval = value >> 64; + retval = negative ? - retval : retval; + return retval; + } + + /** + * Round to the nearest int. + * Similar to std::round this rounds halfway cases away from zero, + * regardless of the current (floating) rounding mode. + * \return The value rounded to the nearest int. + */ + int64_t Round (void) const + { + const bool negative = _v < 0; + int64x64_t value = (negative ? -(*this) : *this); + const int64x64_t half (0, 1LL << 63); + value += half; + int64_t retval = value.GetHigh (); + retval = negative ? - retval : retval; + return retval; + } + /** * Multiply this value by a Q0.128 value, presumably representing an inverse, * completing a division operation. diff --git a/src/core/model/int64x64-cairo.h b/src/core/model/int64x64-cairo.h index b8eb80071..53f95fd70 100644 --- a/src/core/model/int64x64-cairo.h +++ b/src/core/model/int64x64-cairo.h @@ -226,6 +226,37 @@ public: return _v.lo; } + /** + * Truncate to an integer. + * Truncation is always toward zero, + * \return The value truncated toward zero. + */ + int64_t GetInt (void) const + { + const bool negative = _cairo_int128_negative (_v); + const cairo_int128_t value = negative ? _cairo_int128_negate (_v) : _v; + int64_t retval = value.hi; + retval = negative ? - retval : retval; + return retval; + } + + /** + * Round to the nearest int. + * Similar to std::round this rounds halfway cases away from zero, + * regardless of the current (floating) rounding mode. + * \return The value rounded to the nearest int. + */ + int64_t Round (void) const + { + const bool negative = _cairo_int128_negative (_v); + cairo_uint128_t value = negative ? _cairo_int128_negate (_v) : _v; + cairo_uint128_t half {1ULL << 63, 0}; // lo, hi + value = _cairo_uint128_add (value, half); + int64_t retval = value.hi; + retval = negative ? - retval : retval; + return retval; + } + /** * Multiply this value by a Q0.128 value, presumably representing an inverse, * completing a division operation. diff --git a/src/core/model/int64x64-double.h b/src/core/model/int64x64-double.h index 42b8df3f3..34cd9a464 100644 --- a/src/core/model/int64x64-double.h +++ b/src/core/model/int64x64-double.h @@ -226,6 +226,29 @@ public: return GetHighLow ().second; } + /** + * Truncate to an integer. + * Truncation is always toward zero, + * \return The value truncated toward zero. + */ + int64_t GetInt (void) const + { + int64_t retval = static_cast (_v); + return retval; + } + + /** + * Round to the nearest int. + * Similar to std::round this rounds halfway cases away from zero, + * regardless of the current (floating) rounding mode. + * \return The value rounded to the nearest int. + */ + int64_t Round (void) const + { + int64_t retval = std::round (_v); + return retval; + } + /** * Multiply this value by a Q0.128 value, presumably representing an inverse, * completing a division operation. diff --git a/src/core/test/int64x64-test-suite.cc b/src/core/test/int64x64-test-suite.cc index bfa17d03f..9539244c6 100644 --- a/src/core/test/int64x64-test-suite.cc +++ b/src/core/test/int64x64-test-suite.cc @@ -187,6 +187,68 @@ Int64x64HiLoTestCase::DoRun (void) } +class Int64x64IntRoundTestCase : public TestCase +{ +public: + Int64x64IntRoundTestCase (void); + virtual void DoRun (void); + void Check (const int64x64_t value, + const int64_t expectInt, + const int64_t expectRnd); +}; + +Int64x64IntRoundTestCase::Int64x64IntRoundTestCase (void) + : TestCase ("Check GetInt and Round") +{} + +void +Int64x64IntRoundTestCase::Check (const int64x64_t value, + const int64_t expectInt, + const int64_t expectRnd) +{ + int64_t vInt = value.GetInt (); + int64_t vRnd = value.Round (); + + bool pass = (vInt == expectInt) && (vRnd == expectRnd); + std::cout << GetParent ()->GetName () << " Check: " + << (pass ? "pass " : "FAIL ") + << value + << " (int)-> " << std::setw (2) << vInt << " (expected: " << std::setw (2) << expectInt + << "), (rnd)-> " << std::setw (2) << vRnd << " (expected " << std::setw (2) << expectRnd + << ")" + << std::endl; + + NS_TEST_EXPECT_MSG_EQ (vInt, expectInt, + "Truncation to int failed"); + NS_TEST_EXPECT_MSG_EQ (vRnd, expectRnd, + "Rounding to int failed."); +} + +void +Int64x64IntRoundTestCase::DoRun (void) +{ + std::cout << std::endl; + std::cout << GetParent ()->GetName () << " Check: " << GetName () + << std::endl; + + // Trivial cases + Check ( 0, 0, 0); + Check ( 1, 1, 1); + Check (-1, -1, -1); + + // Both should move toward zero + Check ( 2.4, 2, 2); + Check (-2.4, -2, -2); + + // GetInt should move toward zero; Round should move away + Check ( 3.6, 3, 4); + Check (-3.6, -3, -4); + // Boundary case + Check ( 4.5, 4, 5); + Check (-4.5, -4, -5); +} + + class Int64x64InputTestCase : public TestCase { public: @@ -1224,6 +1286,7 @@ public: { AddTestCase (new Int64x64ImplTestCase (), TestCase::QUICK); AddTestCase (new Int64x64HiLoTestCase (), TestCase::QUICK); + AddTestCase (new Int64x64IntRoundTestCase (), TestCase::QUICK); AddTestCase (new Int64x64ArithmeticTestCase (), TestCase::QUICK); AddTestCase (new Int64x64CompareTestCase (), TestCase::QUICK); AddTestCase (new Int64x64InputTestCase (), TestCase::QUICK);