aboutsummaryrefslogtreecommitdiff
path: root/llvm/unittests/ADT
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/unittests/ADT')
-rw-r--r--llvm/unittests/ADT/APFloatTest.cpp1715
-rw-r--r--llvm/unittests/ADT/APIntTest.cpp125
-rw-r--r--llvm/unittests/ADT/DenseMapTest.cpp52
-rw-r--r--llvm/unittests/ADT/SmallPtrSetTest.cpp4
-rw-r--r--llvm/unittests/ADT/StringRefTest.cpp13
5 files changed, 1813 insertions, 96 deletions
diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index 9609e8e..141282e 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -16,9 +16,11 @@
#include "llvm/Support/FormatVariadic.h"
#include "gtest/gtest.h"
#include <cmath>
+#include <limits>
#include <ostream>
#include <string>
#include <tuple>
+#include <type_traits>
using namespace llvm;
@@ -1658,6 +1660,90 @@ TEST(APFloatTest, toInteger) {
EXPECT_EQ(APSInt::getMaxValue(5, false), result);
}
+class APFloatConvertFromAPIntParamTest
+ : public ::testing::TestWithParam<const fltSemantics *> {
+protected:
+ // Helper to run a conversion and compare the integer result directly.
+ static void testConversionAndCompareInt(const APInt &InputValue,
+ const bool IsSigned,
+ APFloat::roundingMode RM,
+ const APInt &ExpectedIntValue) {
+ const fltSemantics &Sem = *GetParam();
+ APFloat F(Sem);
+ F.convertFromAPInt(InputValue, /*IsSigned=*/IsSigned, RM);
+
+ APSInt ResultInt(InputValue.getBitWidth(), /*isUnsigned=*/!IsSigned);
+ bool IsExact;
+ F.convertToInteger(ResultInt, APFloat::rmTowardZero, &IsExact);
+
+ EXPECT_TRUE(IsExact);
+ EXPECT_TRUE(ResultInt.eq(ExpectedIntValue))
+ << "InputValue: " << InputValue << "\n"
+ << ResultInt << " vs " << ExpectedIntValue << "\n";
+ }
+};
+
+TEST_P(APFloatConvertFromAPIntParamTest, HalfwayRounding) {
+ const fltSemantics &Sem = *GetParam();
+ const unsigned Precision = APFloat::semanticsPrecision(Sem);
+
+ if (Precision == 0)
+ GTEST_SKIP() << "Skipping test for semantics with no significand.";
+
+ for (bool IsSigned : {false, true}) {
+ const unsigned BitWidth = Precision + 1 + (IsSigned ? 1 : 0);
+
+ const APInt RoundedDownVal = APInt::getOneBitSet(BitWidth, Precision);
+ const APInt HalfwayVal = RoundedDownVal + 1;
+ const APInt RoundedUpVal = RoundedDownVal + 2;
+
+ testConversionAndCompareInt(HalfwayVal, IsSigned,
+ APFloat::rmNearestTiesToEven, RoundedDownVal);
+ testConversionAndCompareInt(HalfwayVal, IsSigned,
+ APFloat::rmNearestTiesToAway, RoundedUpVal);
+ testConversionAndCompareInt(HalfwayVal, IsSigned, APFloat::rmTowardPositive,
+ RoundedUpVal);
+ testConversionAndCompareInt(HalfwayVal, IsSigned, APFloat::rmTowardNegative,
+ RoundedDownVal);
+ testConversionAndCompareInt(HalfwayVal, IsSigned, APFloat::rmTowardZero,
+ RoundedDownVal);
+ }
+}
+
+TEST_P(APFloatConvertFromAPIntParamTest, MaxMagnitude) {
+ const fltSemantics &Sem = *GetParam();
+ const unsigned Precision = APFloat::semanticsPrecision(Sem);
+
+ if (Precision == 0)
+ GTEST_SKIP() << "Skipping test for semantics with no significand.";
+
+ const APFloat Largest = APFloat::getLargest(Sem, /*Negative=*/false);
+ const int Exp = ilogb(Largest);
+ for (bool IsSigned : {false, true}) {
+ const unsigned BitWidth = Exp + 1 + (IsSigned ? 1 : 0);
+
+ bool IsExact;
+ APSInt LargestAsInt{BitWidth, /*IsUnsigned=*/!IsSigned};
+ const APFloat::opStatus ToIntStatus =
+ Largest.convertToInteger(LargestAsInt, APFloat::rmTowardZero, &IsExact);
+ EXPECT_EQ(ToIntStatus, APFloat::opOK);
+
+ for (const APFloat::roundingMode RM :
+ {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative,
+ APFloat::rmTowardPositive, APFloat::rmTowardZero,
+ APFloat::rmNearestTiesToEven}) {
+ testConversionAndCompareInt(LargestAsInt, IsSigned, RM, LargestAsInt);
+ }
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(IEEESemantics, APFloatConvertFromAPIntParamTest,
+ ::testing::Values(&APFloat::IEEEhalf(),
+ &APFloat::BFloat(),
+ &APFloat::IEEEsingle(),
+ &APFloat::IEEEdouble(),
+ &APFloat::IEEEquad()));
+
static APInt nanbitsFromAPInt(const fltSemantics &Sem, bool SNaN, bool Negative,
uint64_t payload) {
APInt appayload(64, payload);
@@ -1916,6 +2002,15 @@ TEST(APFloatTest, exactInverse) {
EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::PPCDoubleDouble(), "0.5")));
EXPECT_TRUE(APFloat(APFloat::x87DoubleExtended(), "2.0").getExactInverse(&inv));
EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(APFloat::x87DoubleExtended(), "0.5")));
+ // 0x1p1022 has a normal inverse for IEEE 754 binary64: 0x1p-1022.
+ EXPECT_TRUE(APFloat(0x1p1022).getExactInverse(&inv));
+ EXPECT_TRUE(inv.bitwiseIsEqual(APFloat(0x1p-1022)));
+ // With regards to getExactInverse, IEEEdouble and PPCDoubleDouble should
+ // behave the same.
+ EXPECT_TRUE(
+ APFloat(APFloat::PPCDoubleDouble(), "0x1p1022").getExactInverse(&inv));
+ EXPECT_TRUE(
+ inv.bitwiseIsEqual(APFloat(APFloat::PPCDoubleDouble(), "0x1p-1022")));
// FLT_MIN
EXPECT_TRUE(APFloat(1.17549435e-38f).getExactInverse(&inv));
@@ -1927,6 +2022,8 @@ TEST(APFloatTest, exactInverse) {
EXPECT_FALSE(APFloat(0.0).getExactInverse(nullptr));
// Denormalized float
EXPECT_FALSE(APFloat(1.40129846e-45f).getExactInverse(nullptr));
+ // Largest subnormal
+ EXPECT_FALSE(APFloat(0x1p-127f).getExactInverse(nullptr));
}
TEST(APFloatTest, roundToIntegral) {
@@ -2661,6 +2758,37 @@ TEST(APFloatTest, Float8UZConvert) {
}
}
+struct DD {
+ double Hi;
+ double Lo;
+};
+
+template <typename T, typename U>
+static APFloat makeDoubleAPFloat(T Hi, U Lo) {
+ APFloat HiFloat{APFloat::IEEEdouble(), APFloat::uninitialized};
+ if constexpr (std::is_same_v<decltype(Hi), APFloat>) {
+ HiFloat = Hi;
+ } else if constexpr (std::is_same_v<decltype(Hi), double>) {
+ HiFloat = APFloat{Hi};
+ } else {
+ HiFloat = {APFloat::IEEEdouble(), Hi};
+ }
+
+ APFloat LoFloat{APFloat::IEEEdouble(), APFloat::uninitialized};
+ if constexpr (std::is_same_v<decltype(Lo), APFloat>) {
+ LoFloat = Lo;
+ } else if constexpr (std::is_same_v<decltype(Lo), double>) {
+ LoFloat = APFloat{Lo};
+ } else {
+ LoFloat = {APFloat::IEEEdouble(), Lo};
+ }
+
+ APInt Bits = LoFloat.bitcastToAPInt().concat(HiFloat.bitcastToAPInt());
+ return APFloat(APFloat::PPCDoubleDouble(), Bits);
+}
+
+static APFloat makeDoubleAPFloat(DD X) { return makeDoubleAPFloat(X.Hi, X.Lo); }
+
TEST(APFloatTest, PPCDoubleDouble) {
APFloat test(APFloat::PPCDoubleDouble(), "1.0");
EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]);
@@ -5315,18 +5443,809 @@ TEST(APFloatTest, PPCDoubleDoubleFMA) {
APFloat(APFloat::PPCDoubleDouble(), "10").compare(A));
}
-TEST(APFloatTest, PPCDoubleDoubleRoundToIntegral) {
- {
- APFloat A(APFloat::PPCDoubleDouble(), "1.5");
- A.roundToIntegral(APFloat::rmNearestTiesToEven);
- EXPECT_EQ(APFloat::cmpEqual,
- APFloat(APFloat::PPCDoubleDouble(), "2").compare(A));
+struct PPCDoubleDoubleRoundToIntegralTestCase {
+ DD Input;
+ DD Rounded[5] = {};
+ constexpr PPCDoubleDoubleRoundToIntegralTestCase &
+ withRounded(DD R, APFloat::roundingMode RM) {
+ Rounded[static_cast<std::underlying_type_t<APFloat::roundingMode>>(RM)] = R;
+ return *this;
}
- {
- APFloat A(APFloat::PPCDoubleDouble(), "2.5");
- A.roundToIntegral(APFloat::rmNearestTiesToEven);
- EXPECT_EQ(APFloat::cmpEqual,
- APFloat(APFloat::PPCDoubleDouble(), "2").compare(A));
+};
+
+auto ppcDoubleDoubleRoundToIntegralTests() {
+ constexpr double Eps = std::numeric_limits<double>::epsilon();
+ constexpr double HalfEps = Eps / 2.0;
+ constexpr double QuarterEps = Eps / 4.0;
+ constexpr double SmallestNormal = std::numeric_limits<double>::min();
+ constexpr double EvenIntegerThreshold{uint64_t{1}
+ << std::numeric_limits<double>::digits};
+ constexpr double Inf = std::numeric_limits<double>::infinity();
+ constexpr double QNaN = std::numeric_limits<double>::quiet_NaN();
+ using TestCase = PPCDoubleDoubleRoundToIntegralTestCase;
+ static constexpr auto TestCases = std::array{
+ // 1. Zeros and Basic Integers
+ // Input: Positive Zero (0.0, 0.0)
+ TestCase({{0.0, 0.0}})
+ .withRounded({0.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({0.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({0.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({0.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({0.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Negative Zero (-0.0, 0.0)
+ TestCase({{-0.0, 0.0}})
+ .withRounded({-0.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-0.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-0.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-0.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-0.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Positive Even (2.0, 0.0)
+ TestCase({{2.0, 0.0}})
+ .withRounded({2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Positive Odd (3.0, 0.0)
+ TestCase({{3.0, 0.0}})
+ .withRounded({3.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({3.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({3.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Negative Even (-2.0, 0.0)
+ TestCase({{-2.0, 0.0}})
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // 2. General Fractions (Non-Ties)
+ // Input: 2.3
+ TestCase({{2.3, 0.0}})
+ .withRounded({2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({3.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: 2.7
+ TestCase({{2.7, 0.0}})
+ .withRounded({2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({3.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: -2.3
+ TestCase({{-2.3, 0.0}})
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-3.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: -2.7
+ TestCase({{-2.7, 0.0}})
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-3.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-3.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-3.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: 2.3 + Tiny
+ TestCase({{2.3, SmallestNormal}})
+ .withRounded({2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({3.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // 3. Exact Midpoints (Ties at N.5)
+ // Input: 0.5
+ TestCase({{0.5, 0.0}})
+ .withRounded({0.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({0.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({1.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({0.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: 1.5 (Odd base)
+ TestCase({{1.5, 0.0}})
+ .withRounded({1.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({1.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: 2.5 (Even base)
+ TestCase({{2.5, 0.0}})
+ .withRounded({2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({3.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: -0.5
+ TestCase({{-0.5, 0.0}})
+ .withRounded({-0.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-1.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-0.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-1.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-0.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: -1.5 (Odd base)
+ TestCase({{-1.5, 0.0}})
+ .withRounded({-1.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-1.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: -2.5 (Even base)
+ TestCase({{-2.5, 0.0}})
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-3.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-3.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // 4. Near Midpoints (lo breaks the tie)
+ // Input: Slightly > 2.5
+ TestCase({{2.5, SmallestNormal}})
+ .withRounded({2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({3.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({3.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Slightly < 2.5
+ TestCase({{2.5, -SmallestNormal}})
+ .withRounded({2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({3.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Slightly > 1.5
+ TestCase({{1.5, SmallestNormal}})
+ .withRounded({1.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({1.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Slightly < 1.5
+ TestCase({{1.5, -SmallestNormal}})
+ .withRounded({1.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({1.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Slightly > -2.5 (closer to 0)
+ TestCase({{-2.5, SmallestNormal}})
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-3.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Slightly < -2.5 (further from 0)
+ TestCase({{-2.5, -SmallestNormal}})
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-3.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-3.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-3.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // 5. Near Integers (lo crosses the integer boundary)
+ // Input: Slightly > 2.0
+ TestCase({{2.0, SmallestNormal}})
+ .withRounded({2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({3.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Slightly < 2.0 (1.99...)
+ TestCase({{2.0, -SmallestNormal}})
+ .withRounded({1.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({1.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Slightly > -2.0 (-1.99...)
+ TestCase({{-2.0, SmallestNormal}})
+ .withRounded({-1.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-1.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Slightly < -2.0
+ TestCase({{-2.0, -SmallestNormal}})
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-3.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-2.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Slightly > 0.0
+ TestCase({{SmallestNormal, 0.0}})
+ .withRounded({0.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({0.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({1.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({0.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({0.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: Slightly < 0.0
+ TestCase({{-SmallestNormal, 0.0}})
+ .withRounded({-0.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-1.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-0.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-0.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-0.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // 6. Boundary of Canonicalization (Maximum lo)
+ // Input: 1.0 + Max lo (1 + 2^-53)
+ TestCase({{1.0, HalfEps}})
+ .withRounded({1.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({1.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({2.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: 1.0 - Max lo (1 - 2^-54)
+ TestCase({{1.0, -QuarterEps}})
+ .withRounded({0.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({0.0, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({1.0, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({1.0, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // 7. Large Magnitudes (Beyond 2^53). N = EvenIntegerThreshold (Even)
+ // Input: EvenIntegerThreshold (Exact)
+ TestCase({{EvenIntegerThreshold, 0.0}})
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardZero)
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({EvenIntegerThreshold, 0.0},
+ APFloat::rmNearestTiesToAway)
+ .withRounded({EvenIntegerThreshold, 0.0},
+ APFloat::rmNearestTiesToEven),
+
+ // Input: EvenIntegerThreshold+1 (Exact)
+ TestCase({{EvenIntegerThreshold, 1.0}})
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardZero)
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardNegative)
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive)
+ .withRounded({EvenIntegerThreshold, 1.0},
+ APFloat::rmNearestTiesToAway)
+ .withRounded({EvenIntegerThreshold, 1.0},
+ APFloat::rmNearestTiesToEven),
+
+ // Fractions
+ // Input: EvenIntegerThreshold+0.25
+ TestCase({{EvenIntegerThreshold, 0.25}})
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardZero)
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive)
+ .withRounded({EvenIntegerThreshold, 0.0},
+ APFloat::rmNearestTiesToAway)
+ .withRounded({EvenIntegerThreshold, 0.0},
+ APFloat::rmNearestTiesToEven),
+
+ // Input: EvenIntegerThreshold+0.75
+ TestCase({{EvenIntegerThreshold, 0.75}})
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardZero)
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive)
+ .withRounded({EvenIntegerThreshold, 1.0},
+ APFloat::rmNearestTiesToAway)
+ .withRounded({EvenIntegerThreshold, 1.0},
+ APFloat::rmNearestTiesToEven),
+
+ // Ties (Midpoints)
+ // Input: EvenIntegerThreshold-0.5
+ TestCase({{EvenIntegerThreshold, -0.5}})
+ .withRounded({EvenIntegerThreshold - 1.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({EvenIntegerThreshold - 1.0, 0.0},
+ APFloat::rmTowardNegative)
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({EvenIntegerThreshold, 0.0},
+ APFloat::rmNearestTiesToAway)
+ .withRounded({EvenIntegerThreshold, 0.0},
+ APFloat::rmNearestTiesToEven),
+
+ // Input: EvenIntegerThreshold+0.5
+ TestCase({{EvenIntegerThreshold, 0.5}})
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardZero)
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive)
+ .withRounded({EvenIntegerThreshold, 1.0},
+ APFloat::rmNearestTiesToAway)
+ .withRounded({EvenIntegerThreshold, 0.0},
+ APFloat::rmNearestTiesToEven),
+
+ // Input: EvenIntegerThreshold+1.5
+ TestCase({{EvenIntegerThreshold + 2.0, -0.5}})
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardZero)
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardNegative)
+ .withRounded({EvenIntegerThreshold + 2.0, 0.0},
+ APFloat::rmTowardPositive)
+ .withRounded({EvenIntegerThreshold + 2.0, 0.0},
+ APFloat::rmNearestTiesToAway)
+ .withRounded({EvenIntegerThreshold + 2.0, 0.0},
+ APFloat::rmNearestTiesToEven),
+
+ // Input: EvenIntegerThreshold+2.5
+ TestCase({{EvenIntegerThreshold + 2.0, 0.5}})
+ .withRounded({EvenIntegerThreshold + 2.0, 0.0}, APFloat::rmTowardZero)
+ .withRounded({EvenIntegerThreshold + 2.0, 0.0},
+ APFloat::rmTowardNegative)
+ .withRounded({EvenIntegerThreshold + 4.0, -1.0},
+ APFloat::rmTowardPositive)
+ .withRounded({EvenIntegerThreshold + 4.0, -1.0},
+ APFloat::rmNearestTiesToAway)
+ .withRounded({EvenIntegerThreshold + 2.0, 0.0},
+ APFloat::rmNearestTiesToEven),
+
+ // Near Ties
+ // Input: EvenIntegerThreshold+0.5+HalfEps
+ TestCase({{EvenIntegerThreshold, 0.5 + HalfEps}})
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardZero)
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive)
+ .withRounded({EvenIntegerThreshold, 1.0},
+ APFloat::rmNearestTiesToAway)
+ .withRounded({EvenIntegerThreshold, 1.0},
+ APFloat::rmNearestTiesToEven),
+
+ // Input: EvenIntegerThreshold+0.5-QuarterEps
+ TestCase({{EvenIntegerThreshold, 0.5 - QuarterEps}})
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardZero)
+ .withRounded({EvenIntegerThreshold, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive)
+ .withRounded({EvenIntegerThreshold, 0.0},
+ APFloat::rmNearestTiesToAway)
+ .withRounded({EvenIntegerThreshold, 0.0},
+ APFloat::rmNearestTiesToEven),
+
+ // Canonical Boundary (Max lo for EvenIntegerThreshold is 1.0)
+ // Input: EvenIntegerThreshold+1.0
+ TestCase({{EvenIntegerThreshold, 1.0}})
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardZero)
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardNegative)
+ .withRounded({EvenIntegerThreshold, 1.0}, APFloat::rmTowardPositive)
+ .withRounded({EvenIntegerThreshold, 1.0},
+ APFloat::rmNearestTiesToAway)
+ .withRounded({EvenIntegerThreshold, 1.0},
+ APFloat::rmNearestTiesToEven),
+
+ // 8. Special Values
+ // Input: +Inf
+ TestCase({{Inf, 0.0}})
+ .withRounded({Inf, 0.0}, APFloat::rmTowardZero)
+ .withRounded({Inf, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({Inf, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({Inf, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({Inf, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: -Inf
+ TestCase({{-Inf, 0.0}})
+ .withRounded({-Inf, 0.0}, APFloat::rmTowardZero)
+ .withRounded({-Inf, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({-Inf, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({-Inf, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({-Inf, 0.0}, APFloat::rmNearestTiesToEven),
+
+ // Input: NaN input hi. Expected output canonical (NaN, 0.0).
+ TestCase({{QNaN, 0.0}})
+ .withRounded({QNaN, 0.0}, APFloat::rmTowardZero)
+ .withRounded({QNaN, 0.0}, APFloat::rmTowardNegative)
+ .withRounded({QNaN, 0.0}, APFloat::rmTowardPositive)
+ .withRounded({QNaN, 0.0}, APFloat::rmNearestTiesToAway)
+ .withRounded({QNaN, 0.0}, APFloat::rmNearestTiesToEven),
+ };
+ return TestCases;
+}
+
+class PPCDoubleDoubleRoundToIntegralValueTest
+ : public testing::Test,
+ public ::testing::WithParamInterface<
+ PPCDoubleDoubleRoundToIntegralTestCase> {};
+
+INSTANTIATE_TEST_SUITE_P(
+ PPCDoubleDoubleRoundToIntegralValueParamTests,
+ PPCDoubleDoubleRoundToIntegralValueTest,
+ ::testing::ValuesIn(ppcDoubleDoubleRoundToIntegralTests()));
+
+TEST_P(PPCDoubleDoubleRoundToIntegralValueTest,
+ PPCDoubleDoubleRoundToIntegral) {
+ const PPCDoubleDoubleRoundToIntegralTestCase TestCase = GetParam();
+ const APFloat Input = makeDoubleAPFloat(TestCase.Input);
+ EXPECT_FALSE(Input.isDenormal())
+ << TestCase.Input.Hi << " + " << TestCase.Input.Lo;
+ for (size_t I = 0, E = std::size(TestCase.Rounded); I != E; ++I) {
+ const auto RM = static_cast<APFloat::roundingMode>(I);
+ const APFloat Expected = makeDoubleAPFloat(TestCase.Rounded[I]);
+ EXPECT_FALSE(Expected.isDenormal())
+ << TestCase.Rounded[I].Hi << " + " << TestCase.Input.Lo;
+ APFloat Actual = Input;
+ Actual.roundToIntegral(RM);
+ if (Actual.isNaN())
+ EXPECT_TRUE(Actual.isNaN());
+ else
+ EXPECT_EQ(Actual.compare(Expected), APFloat::cmpEqual)
+ << "RM: " << RM << " Input.Hi: " << TestCase.Input.Hi
+ << " Input.Lo: " << TestCase.Input.Lo << " Actual: " << Actual
+ << " Expected.Hi: " << TestCase.Rounded[I].Hi
+ << " Expected.Lo: " << TestCase.Rounded[I].Lo
+ << " Expected: " << Expected;
+ }
+}
+
+namespace PPCDoubleDoubleConvertToIntegerTestDetails {
+// Define the rounding modes for easier readability.
+static constexpr auto RNE = APFloat::rmNearestTiesToEven;
+static constexpr auto RNA = APFloat::rmNearestTiesToAway;
+static constexpr auto RTZ = APFloat::rmTowardZero;
+static constexpr auto RUP = APFloat::rmTowardPositive;
+static constexpr auto RDN = APFloat::rmTowardNegative;
+
+struct TestCase {
+ // Structure to hold the expected result of a conversion
+ struct ExpectedConversion {
+ // The expected integer value represented as a string (decimal).
+ // We use a string to easily represent arbitrary precision values in
+ // constexpr. The test runner should parse this into an APSInt matching the
+ // test configuration.
+ const char *ExpectedIntStr;
+ APFloat::opStatus Status;
+ };
+
+ DD Input;
+ unsigned IntegerWidth;
+ bool IsSigned;
+ // Array indexed by the rounding mode enum value.
+ std::array<ExpectedConversion, 5> Rounded = {};
+
+ // Helper to define the expected results for a specific rounding mode.
+ constexpr TestCase &with(APFloat::roundingMode RM, const char *ExpectedStr,
+ APFloat::opStatus Status) {
+ Rounded[static_cast<std::underlying_type_t<APFloat::roundingMode>>(RM)] = {
+ ExpectedStr,
+ Status,
+ };
+ return *this;
+ }
+
+ // Helper to define the same result for all rounding modes.
+ constexpr TestCase &withAll(const char *ExpectedStr,
+ APFloat::opStatus Status) {
+ return with(RNE, ExpectedStr, Status)
+ .with(RNA, ExpectedStr, Status)
+ .with(RTZ, ExpectedStr, Status)
+ .with(RUP, ExpectedStr, Status)
+ .with(RDN, ExpectedStr, Status);
+ }
+};
+
+auto testCases() {
+ // Define the status codes.
+ constexpr auto OK = llvm::APFloat::opOK;
+ constexpr auto Inexact = llvm::APFloat::opInexact;
+ // The API specifies opInvalidOp for out-of-range (overflow/underflow) and
+ // NaN.
+ constexpr auto Invalid = llvm::APFloat::opInvalidOp;
+
+ // Helper constants for constructing specific DD values.
+ constexpr double Infinity = std::numeric_limits<double>::infinity();
+ constexpr double NaN = std::numeric_limits<double>::quiet_NaN();
+ constexpr double DMAX = std::numeric_limits<double>::max();
+
+ // Powers of 2
+ constexpr double P53 = 0x1p53;
+ constexpr double P63 = 0x1p63;
+ constexpr double P64 = 0x1p64;
+ // 2^-100 (A very small delta demonstrating extended precision)
+ constexpr double PM100 = 0x1p-100;
+
+ static constexpr auto ConvertToIntegerTestCases = std::array{
+ // 1. Zeros, NaNs, and Infinities (Target: 64-bit Signed)
+ // INT64_MAX = 9223372036854775807
+ // INT64_MIN = -9223372036854775808
+
+ // Input: Positive Zero (0.0, 0.0)
+ TestCase{{0.0, 0.0}, 64, true}.withAll("0", OK),
+
+ // Input: Negative Zero (-0.0, 0.0)
+ TestCase{{-0.0, 0.0}, 64, true}.withAll("0", OK),
+
+ // Input: NaN. Expected behavior: Invalid, deterministic result of 0.
+ TestCase{{NaN, 0.0}, 64, true}.withAll("0", Invalid),
+
+ // Input: +Infinity. Expected behavior: Invalid, deterministic result of
+ // INT64_MAX.
+ TestCase{{Infinity, 0.0}, 64, true}.withAll("9223372036854775807",
+ Invalid),
+
+ // Input: -Infinity. Expected behavior: Invalid, deterministic result of
+ // INT64_MIN.
+ TestCase{{-Infinity, 0.0}, 64, true}.withAll("-9223372036854775808",
+ Invalid),
+
+ // 2. Basic Rounding and Tie-Breaking (Target: 32-bit Signed)
+
+ // Input: 2.5 (Tie, preceding integer is Even)
+ TestCase{{2.5, 0.0}, 32, true}
+ .with(RTZ, "2", Inexact)
+ .with(RDN, "2", Inexact)
+ .with(RUP, "3", Inexact)
+ .with(RNA, "3", Inexact)
+ .with(RNE, "2", Inexact),
+
+ // Input: 3.5 (Tie, preceding integer is Odd)
+ TestCase{{3.5, 0.0}, 32, true}
+ .with(RTZ, "3", Inexact)
+ .with(RDN, "3", Inexact)
+ .with(RUP, "4", Inexact)
+ .with(RNA, "4", Inexact)
+ .with(RNE, "4", Inexact),
+
+ // Input: -2.5 (Tie, preceding integer is Even)
+ TestCase{{-2.5, 0.0}, 32, true}
+ .with(RTZ, "-2", Inexact)
+ .with(RDN, "-3", Inexact)
+ .with(RUP, "-2", Inexact)
+ .with(RNA, "-3", Inexact)
+ .with(RNE, "-2", Inexact),
+
+ // 3. Double-Double Precision (The role of 'lo')
+ // Testing how extended precision affects rounding decisions.
+
+ // Input: 2.5 + Epsilon (Slightly above tie)
+ TestCase{{2.5, PM100}, 32, true}
+ .with(RTZ, "2", Inexact)
+ .with(RDN, "2", Inexact)
+ .with(RUP, "3", Inexact)
+ .with(RNA, "3", Inexact)
+ .with(RNE, "3", Inexact),
+
+ // Input: 2.5 - Epsilon (Slightly below tie)
+ TestCase{{2.5, -PM100}, 32, true}
+ .with(RTZ, "2", Inexact)
+ .with(RDN, "2", Inexact)
+ .with(RUP, "3", Inexact)
+ .with(RNA, "2", Inexact)
+ .with(RNE, "2", Inexact),
+
+ // Input: 1.0 + Epsilon (Just above 1.0, e.g., 1.00...1)
+ TestCase{{1.0, PM100}, 32, true}
+ .with(RTZ, "1", Inexact)
+ .with(RDN, "1", Inexact)
+ .with(RUP, "2", Inexact)
+ .with(RNA, "1", Inexact)
+ .with(RNE, "1", Inexact),
+
+ // Input: 1.0 - Epsilon (Just below 1.0, e.g. 0.999...)
+ TestCase{{1.0, -PM100}, 32, true}
+ .with(RTZ, "0", Inexact)
+ .with(RDN, "0", Inexact)
+ .with(RUP, "1", Inexact)
+ .with(RNA, "1", Inexact)
+ .with(RNE, "1", Inexact),
+
+ // Input: Large number tie-breaking (Crucial test for DD implementation)
+ // Input: 2^53 + 1.5.
+ // A standard double(2^53 + 1.5) rounds to 2^53 + 2.0.
+ // The DD representation must precisely hold 2^53 + 1.5.
+ // The canonical DD representation is {2^53 + 2.0, -0.5}.
+ // Value is 9007199254740993.5
+ TestCase{{P53 + 2.0, -0.5}, 64, true}
+ .with(RTZ, "9007199254740993", Inexact)
+ .with(RDN, "9007199254740993", Inexact)
+ .with(RUP, "9007199254740994", Inexact)
+ .with(RNA, "9007199254740994", Inexact)
+ .with(RNE, "9007199254740994", Inexact),
+
+ // 4. Overflow Boundaries (Signed)
+
+ // Input: Exactly INT64_MAX. (2^63 - 1)
+ // Represented precisely as (2^63, -1.0)
+ TestCase{{P63, -1.0}, 64, true}.withAll("9223372036854775807", OK),
+
+ // Input: INT64_MAX + 0.3.
+ // Represented as (2^63, -0.7)
+ TestCase{{P63, -0.7}, 64, true}
+ .with(RTZ, "9223372036854775807", Inexact)
+ .with(RDN, "9223372036854775807", Inexact)
+ .with(RNA, "9223372036854775807", Inexact)
+ .with(RNE, "9223372036854775807", Inexact)
+ .with(RUP, "9223372036854775807", Invalid),
+
+ // Input: INT64_MAX + 0.5 (Tie at the boundary)
+ // Represented as (2^63, -0.5). Target integers are MAX (odd) and 2^63
+ // (even).
+ TestCase{{P63, -0.5}, 64, true}
+ .with(RTZ, "9223372036854775807", Inexact)
+ .with(RDN, "9223372036854775807", Inexact)
+ .with(RUP, "9223372036854775807", Invalid)
+ .with(RNA, "9223372036854775807", Invalid)
+ .with(RNE, "9223372036854775807", Invalid),
+
+ // Input: 2^55 - 2^1 - 2^-52 to signed integer.
+ // Represented as (2^55 - 2^2, 2^1 - 2^-1).
+ TestCase{{0x1.fffffffffffffp+54, 0x1.8p0}, 56, true}
+ .with(RTZ, "36028797018963965", Inexact)
+ .with(RDN, "36028797018963965", Inexact)
+ .with(RUP, "36028797018963966", Inexact)
+ .with(RNA, "36028797018963966", Inexact)
+ .with(RNE, "36028797018963966", Inexact),
+
+ // Input: 2^55 - 2^1 - 2^-52 to signed integer.
+ // Represented as (2^55 - 2^2, 2^1 - 2^-52).
+ TestCase{{0x1.fffffffffffffp+54, 0x1.fffffffffffffp0}, 56, true}
+ .with(RTZ, "36028797018963965", Inexact)
+ .with(RDN, "36028797018963965", Inexact)
+ .with(RUP, "36028797018963966", Inexact)
+ .with(RNA, "36028797018963966", Inexact)
+ .with(RNE, "36028797018963966", Inexact),
+
+ // Input: Exactly 2^63 (One past INT64_MAX)
+ TestCase{{P63, 0.0}, 64, true}.withAll("9223372036854775807", Invalid),
+
+ // Input: Exactly INT64_MIN (-2^63)
+ TestCase{{-P63, 0.0}, 64, true}.withAll("-9223372036854775808", OK),
+
+ // Input: INT64_MIN - 0.5 (Tie at the lower boundary)
+ // Target integers are -2^63-1 (odd) and MIN (even).
+ TestCase{{-P63, -0.5}, 64, true}
+ .with(RTZ, "-9223372036854775808", Inexact)
+ .with(RUP, "-9223372036854775808", Inexact)
+ // RDN rounds down, causing overflow.
+ .with(RDN, "-9223372036854775808", Invalid)
+ // RNA rounds away (down), causing overflow.
+ .with(RNA, "-9223372036854775808", Invalid)
+ // RNE rounds to even (up to -2^63), which is OK.
+ .with(RNE, "-9223372036854775808", Inexact),
+
+ // 5. Overflow Boundaries (Unsigned)
+ // UINT64_MAX = 18446744073709551615 (2^64 - 1)
+
+ // Input: Exactly UINT64_MAX. (2^64 - 1)
+ // Represented precisely as (2^64, -1.0)
+ TestCase{{P64, -1.0}, 64, false}.withAll("18446744073709551615", OK),
+
+ // Input: UINT64_MAX + 0.5 (Tie at the boundary)
+ // Represented as (2^64, -0.5)
+ TestCase{{P64, -0.5}, 64, false}
+ .with(RTZ, "18446744073709551615", Inexact)
+ .with(RDN, "18446744073709551615", Inexact)
+ // RUP rounds up (2^64), causing overflow.
+ .with(RUP, "18446744073709551615", Invalid)
+ // RNA rounds away (up), causing overflow.
+ .with(RNA, "18446744073709551615", Invalid)
+ // RNE rounds to even (up to 2^64), causing overflow.
+ .with(RNE, "18446744073709551615", Invalid),
+
+ // Input: 2^55 - 2^1 - 2^-52 to unsigned integer.
+ // Represented as (2^55 - 2^2, 2^1 - 2^-1).
+ TestCase{{0x1.fffffffffffffp+54, 0x1.8p0}, 55, false}
+ .with(RTZ, "36028797018963965", Inexact)
+ .with(RDN, "36028797018963965", Inexact)
+ .with(RUP, "36028797018963966", Inexact)
+ .with(RNA, "36028797018963966", Inexact)
+ .with(RNE, "36028797018963966", Inexact),
+
+ // Input: 2^55 - 2^1 - 2^-52 to unsigned integer.
+ // Represented as (2^55 - 2^2, 2^1 - 2^-52).
+ TestCase{{0x1.fffffffffffffp+54, 0x1.fffffffffffffp0}, 55, false}
+ .with(RTZ, "36028797018963965", Inexact)
+ .with(RDN, "36028797018963965", Inexact)
+ .with(RUP, "36028797018963966", Inexact)
+ .with(RNA, "36028797018963966", Inexact)
+ .with(RNE, "36028797018963966", Inexact),
+
+ // Input: -0.3 (Slightly below zero)
+ TestCase{{-0.3, 0.0}, 64, false}
+ .with(RTZ, "0", Inexact)
+ .with(RUP, "0", Inexact)
+ .with(RNA, "0", Inexact)
+ .with(RNE, "0", Inexact)
+ .with(RDN, "0", Invalid),
+
+ // Input: -0.5 (Tie at zero)
+ TestCase{{-0.5, 0.0}, 64, false}
+ .with(RTZ, "0", Inexact)
+ .with(RUP, "0", Inexact)
+ // RNE rounds to even (0).
+ .with(RNE, "0", Inexact)
+ .with(RDN, "0", Invalid)
+ // RNA rounds away (-1), causing overflow.
+ .with(RNA, "0", Invalid),
+
+ // Input: -1.0 (Negative integer)
+ TestCase{{-1.0, 0.0}, 64, false}.withAll("0", Invalid),
+
+ // 6. High Precision Integers (Target: 128-bit Signed)
+ // INT128_MAX = 170141183460469231731687303715884105727
+
+ // Input: 2^100 (Exactly representable in DD)
+ // 2^100 = 1267650600228229401496703205376.0
+ TestCase{{1267650600228229401496703205376.0, 0.0}, 128, true}.withAll(
+ "1267650600228229401496703205376", OK),
+
+ // Input: DMAX. (Approx 1.8e308).
+ // This is vastly larger than INT128_MAX (Approx 1.7e38).
+ TestCase{{DMAX, 0.0}, 128, true}.withAll(
+ "170141183460469231731687303715884105727", Invalid),
+
+ // Input: Largest semPPCDoubleDoubleLegacy
+ TestCase{{DMAX, 0x1.ffffffffffffep+969}, 128, true}.withAll(
+ "170141183460469231731687303715884105727", Invalid),
+
+ // 7. Round to negative -0
+ TestCase{{-PM100, 0.0}, 32, true}
+ .with(RTZ, "0", Inexact)
+ .with(RUP, "0", Inexact)
+ .with(RNA, "0", Inexact)
+ .with(RNE, "0", Inexact)
+ .with(RDN, "-1", Inexact),
+ };
+ return ConvertToIntegerTestCases;
+}
+} // namespace PPCDoubleDoubleConvertToIntegerTestDetails
+
+class PPCDoubleDoubleConvertToIntegerValueTest
+ : public testing::Test,
+ public ::testing::WithParamInterface<
+ PPCDoubleDoubleConvertToIntegerTestDetails::TestCase> {};
+
+INSTANTIATE_TEST_SUITE_P(
+ PPCDoubleDoubleConvertToIntegerValueParamTests,
+ PPCDoubleDoubleConvertToIntegerValueTest,
+ ::testing::ValuesIn(
+ PPCDoubleDoubleConvertToIntegerTestDetails::testCases()));
+
+TEST_P(PPCDoubleDoubleConvertToIntegerValueTest,
+ PPCDoubleDoubleConvertToInteger) {
+ const PPCDoubleDoubleConvertToIntegerTestDetails::TestCase Params =
+ GetParam();
+ const APFloat Input = makeDoubleAPFloat(Params.Input);
+ EXPECT_FALSE(Input.isDenormal())
+ << Params.Input.Hi << " + " << Params.Input.Lo;
+
+ for (size_t I = 0, E = std::size(Params.Rounded); I != E; ++I) {
+ const auto RM = static_cast<APFloat::roundingMode>(I);
+ const auto &Expected = Params.Rounded[I];
+ APSInt ActualInteger(Params.IntegerWidth, /*isUnsigned=*/!Params.IsSigned);
+
+ APSInt ExpectedInteger{Expected.ExpectedIntStr};
+ EXPECT_LE(ExpectedInteger.getBitWidth(), Params.IntegerWidth);
+ ExpectedInteger = ExpectedInteger.extend(Params.IntegerWidth);
+ if (ExpectedInteger.isUnsigned() && Params.IsSigned) {
+ ExpectedInteger.setIsSigned(Params.IsSigned);
+ EXPECT_FALSE(ExpectedInteger.isNegative());
+ }
+
+ const bool NegativeUnderflow =
+ ExpectedInteger.isZero() && Input.isNegative();
+ const bool ExpectedIsExact =
+ Expected.Status == APFloat::opOK && !NegativeUnderflow;
+ bool ActualIsExact;
+ const auto ActualStatus =
+ Input.convertToInteger(ActualInteger, RM, &ActualIsExact);
+ EXPECT_EQ(ActualStatus, Expected.Status);
+ EXPECT_EQ(ActualIsExact, ExpectedIsExact);
+ EXPECT_EQ(ActualInteger, ExpectedInteger);
}
}
@@ -5372,6 +6291,102 @@ TEST(APFloatTest, PPCDoubleDoubleCompare) {
}
}
+namespace PPCDoubleDoubleCompareAbsoluteValueTestDetails {
+struct TestCase {
+ DD LHS;
+ DD RHS;
+ APFloat::cmpResult Result;
+};
+
+auto testCases() {
+ static constexpr auto CompareAbsoluteValueTestCases = std::array{
+ TestCase{
+ {1.0, 0.0},
+ {1.0, 0.0},
+ APFloat::cmpEqual,
+ },
+ TestCase{
+ {1.0, -0.0},
+ {1.0, +0.0},
+ APFloat::cmpEqual,
+ },
+ TestCase{
+ {1.0, 0.0},
+ {0x1.0000000000001p+0, 0.0},
+ APFloat::cmpLessThan,
+ },
+ TestCase{
+ {0x1.0000000000001p+0, 0.0},
+ {1.0, 0.0},
+ APFloat::cmpGreaterThan,
+ },
+ TestCase{
+ {0x1.0000000000001p+0, +0x1p-1074},
+ {1.0, -0x1p-1074},
+ APFloat::cmpGreaterThan,
+ },
+ TestCase{
+ {0x1.0000000000001p+0, -0x1p-1074},
+ {1.0, +0x1p-1074},
+ APFloat::cmpGreaterThan,
+ },
+ TestCase{
+ {1.0, 0.0},
+ {1.0, -0x1p-1074},
+ APFloat::cmpGreaterThan,
+ },
+ TestCase{
+ {1.0, 0.0},
+ {1.0, +0x1p-1074},
+ APFloat::cmpLessThan,
+ },
+ TestCase{
+ {1.0, +0x1p-1073},
+ {1.0, -0x1p-1074},
+ APFloat::cmpGreaterThan,
+ },
+ TestCase{
+ {1.0, +0x1p-1074},
+ {1.0, -0x1p-1074},
+ APFloat::cmpGreaterThan,
+ },
+ };
+ return CompareAbsoluteValueTestCases;
+}
+} // namespace PPCDoubleDoubleCompareAbsoluteValueTestDetails
+
+class PPCDoubleDoubleCompareAbsoluteValueValueTest
+ : public testing::Test,
+ public ::testing::WithParamInterface<
+ PPCDoubleDoubleCompareAbsoluteValueTestDetails::TestCase> {};
+
+INSTANTIATE_TEST_SUITE_P(
+ PPCDoubleDoubleCompareAbsoluteValueValueParamTests,
+ PPCDoubleDoubleCompareAbsoluteValueValueTest,
+ ::testing::ValuesIn(
+ PPCDoubleDoubleCompareAbsoluteValueTestDetails::testCases()));
+
+TEST_P(PPCDoubleDoubleCompareAbsoluteValueValueTest,
+ PPCDoubleDoubleCompareAbsoluteValue) {
+ auto Param = GetParam();
+ for (bool LHSNegate : {false, true}) {
+ auto LHS = llvm::detail::DoubleAPFloat{APFloat::PPCDoubleDouble(),
+ APFloat{Param.LHS.Hi},
+ APFloat{Param.LHS.Lo}};
+ if (LHSNegate)
+ LHS.changeSign();
+ for (bool RHSNegate : {false, true}) {
+ auto RHS = llvm::detail::DoubleAPFloat{APFloat::PPCDoubleDouble(),
+ APFloat{Param.RHS.Hi},
+ APFloat{Param.RHS.Lo}};
+ if (RHSNegate)
+ RHS.changeSign();
+
+ EXPECT_EQ(LHS.compareAbsoluteValue(RHS), Param.Result);
+ }
+ }
+}
+
TEST(APFloatTest, PPCDoubleDoubleBitwiseIsEqual) {
using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, bool>;
@@ -5525,19 +6540,328 @@ TEST(APFloatTest, PPCDoubleDoubleScalbn) {
EXPECT_EQ(0x3cc8000000000000ull, Result.bitcastToAPInt().getRawData()[1]);
}
-TEST(APFloatTest, PPCDoubleDoubleFrexp) {
- // 3.0 + 3.0 << 53
- uint64_t Input[] = {
- 0x4008000000000000ull, 0x3cb8000000000000ull,
+namespace PPCDoubleDoubleFrexpTestDetails {
+// Define the rounding modes for easier readability.
+static constexpr auto RNE = APFloat::rmNearestTiesToEven;
+static constexpr auto RNA = APFloat::rmNearestTiesToAway;
+static constexpr auto RTZ = APFloat::rmTowardZero;
+static constexpr auto RUP = APFloat::rmTowardPositive;
+static constexpr auto RDN = APFloat::rmTowardNegative;
+
+struct TestCase {
+ // Structure to hold the expected result of a conversion
+ struct ExpectedFractionExponent {
+ DD Fraction;
+ int Exponent;
+ friend APFloat::cmpResult compare(const ExpectedFractionExponent &Lhs,
+ const ExpectedFractionExponent &Rhs) {
+ const APFloat LhsFraction = makeDoubleAPFloat(Lhs.Fraction);
+ const APFloat RhsFraction = makeDoubleAPFloat(Rhs.Fraction);
+ const APFloat::cmpResult FractionRelation =
+ LhsFraction.compare(RhsFraction);
+ if (FractionRelation == APFloat::cmpUnordered)
+ return APFloat::cmpUnordered;
+ if (LhsFraction.isZero() && RhsFraction.isZero())
+ return APFloat::cmpEqual;
+ if (!LhsFraction.isNegative() &&
+ (RhsFraction.isNegative() || RhsFraction.isZero()))
+ return APFloat::cmpGreaterThan;
+ if (!RhsFraction.isNegative() &&
+ (LhsFraction.isNegative() || LhsFraction.isZero()))
+ return APFloat::cmpLessThan;
+ if (Lhs.Exponent > Rhs.Exponent)
+ return LhsFraction.isNegative() ? APFloat::cmpLessThan
+ : APFloat::cmpGreaterThan;
+ if (Lhs.Exponent < Rhs.Exponent)
+ return RhsFraction.isNegative() ? APFloat::cmpGreaterThan
+ : APFloat::cmpLessThan;
+ return FractionRelation;
+ }
};
- int Exp;
- // 0.75 + 0.75 << 53
- APFloat Result =
- frexp(APFloat(APFloat::PPCDoubleDouble(), APInt(128, 2, Input)), Exp,
- APFloat::rmNearestTiesToEven);
- EXPECT_EQ(2, Exp);
- EXPECT_EQ(0x3fe8000000000000ull, Result.bitcastToAPInt().getRawData()[0]);
- EXPECT_EQ(0x3c98000000000000ull, Result.bitcastToAPInt().getRawData()[1]);
+
+ DD Input;
+ // Array indexed by the rounding mode enum value.
+ std::array<ExpectedFractionExponent, 5> Rounded = {};
+
+ // Helper to define the expected results for a specific rounding mode.
+ constexpr TestCase &with(APFloat::roundingMode RM, DD ExpectedDD,
+ int ExpectedExponent) {
+ Rounded[static_cast<std::underlying_type_t<APFloat::roundingMode>>(RM)] = {
+ ExpectedDD,
+ ExpectedExponent,
+ };
+ return *this;
+ }
+
+ // Helper to define the same result for all rounding modes.
+ constexpr TestCase &withAll(DD ExpectedDD, int ExpectedExponent) {
+ return with(RNE, ExpectedDD, ExpectedExponent)
+ .with(RNA, ExpectedDD, ExpectedExponent)
+ .with(RTZ, ExpectedDD, ExpectedExponent)
+ .with(RUP, ExpectedDD, ExpectedExponent)
+ .with(RDN, ExpectedDD, ExpectedExponent);
+ }
+};
+
+auto testCases() {
+ static constexpr auto FrexpTestCases = std::array{
+ // Input: +infinity
+ TestCase{{std::numeric_limits<double>::infinity(), 0.0}}.withAll(
+ {std::numeric_limits<double>::infinity(), 0.0}, INT_MAX),
+
+ // Input: -infinity
+ TestCase{{-std::numeric_limits<double>::infinity(), 0.0}}.withAll(
+ {-std::numeric_limits<double>::infinity(), 0.0}, INT_MAX),
+
+ // Input: NaN
+ TestCase{{std::numeric_limits<double>::quiet_NaN(), 0.0}}.withAll(
+ {std::numeric_limits<double>::quiet_NaN(), 0.0}, INT_MIN),
+
+ // Input: 2^-1074
+ TestCase{{0x1p-1074, 0.0}}.withAll({0x1p-1, 0.0}, -1073),
+ TestCase{{-0x1p-1074, 0.0}}.withAll({-0x1p-1, 0.0}, -1073),
+
+ // Input: (2^1, -2^-1073 + -2^-1074)
+ TestCase{{0x1p1, -0x1.8p-1073}}
+ .withAll({0x1p0, -0x1p-1073}, 1)
+ .with(RNA, {0x1p0, -0x1p-1074}, 1)
+ .with(RUP, {0x1p0, -0x1p-1074}, 1),
+ TestCase{{-0x1p1, 0x1.8p-1073}}
+ .withAll({-0x1p0, 0x1p-1073}, 1)
+ .with(RNA, {-0x1p0, 0x1p-1074}, 1)
+ .with(RDN, {-0x1p0, 0x1p-1074}, 1),
+
+ // Input: (2^1, -2^-1073)
+ TestCase{{0x1p1, -0x1p-1073}}.withAll({0x1p0, -0x1p-1074}, 1),
+
+ // Input: (2^1, -2^-1074)
+ TestCase{{0x1p1, -0x1p-1074}}
+ .withAll({0x1p-1, -0.0}, 2)
+ .with(RDN, {0x1p0, -0x1p-1074}, 1)
+ .with(RTZ, {0x1p0, -0x1p-1074}, 1),
+
+ // Input: (2^2, -2^-1072 + -2^-1073 + -2^-1074)
+ TestCase{{0x1p2, -0x1.cp-1072}}
+ .withAll({0x1p0, -0x1p-1073}, 2)
+ .with(RUP, {0x1p0, -0x1p-1074}, 2),
+
+ // Input: (2^2, -2^-1072 + -2^-1073)
+ TestCase{{0x1p2, -0x1.8p-1072}}
+ .withAll({0x1p0, -0x1p-1073}, 2)
+ .with(RNA, {0x1p0, -0x1p-1074}, 2)
+ .with(RUP, {0x1p0, -0x1p-1074}, 2),
+ TestCase{{-0x1p2, 0x1.8p-1072}}
+ .withAll({-0x1p0, 0x1p-1073}, 2)
+ .with(RNA, {-0x1p0, 0x1p-1074}, 2)
+ .with(RDN, {-0x1p0, 0x1p-1074}, 2),
+
+ // Input: (2^2, -2^-1072 + -2^-1074)
+ TestCase{{0x1p2, -0x1.4cp-1072}}
+ .withAll({0x1p0, -0x1p-1074}, 2)
+ .with(RDN, {0x1p0, -0x1p-1073}, 2)
+ .with(RTZ, {0x1p0, -0x1p-1073}, 2),
+
+ // Input: (2^2, -2^-1072)
+ TestCase{{0x1p2, -0x1p-1072}}.withAll({0x1p0, -0x1p-1074}, 2),
+
+ // Input: (2^2, -2^-1073 + -2^-1074)
+ TestCase{{0x1p2, -0x1.8p-1073}}
+ .withAll({0x1p0, -0x1p-1074}, 2)
+ .with(RUP, {0x1p-1, -0.0}, 3),
+
+ // Input: (2^2, -2^-1073)
+ TestCase{{0x1p2, -0x1p-1073}}
+ .withAll({0x1p-1, -0.0}, 3)
+ .with(RDN, {0x1p0, -0x1p-1074}, 2)
+ .with(RTZ, {0x1p0, -0x1p-1074}, 2),
+
+ // Input: (2^2, -2^-1074)
+ TestCase{{0x1p2, -0x1p-1074}}
+ .withAll({0x1p-1, -0.0}, 3)
+ .with(RDN, {0x1p0, -0x1p-1074}, 2)
+ .with(RTZ, {0x1p0, -0x1p-1074}, 2),
+
+ // Input: 3+3*2^-53 canonicalized to (3+2^-51, -2^-53)
+ // Output: 0.75+0.75*2^-53 canonicalized to (.75+2^-53, -2^-55)
+ TestCase{{0x1.8000000000001p1, -0x1p-53}}.withAll(
+ {0x1.8000000000001p-1, -0x1p-55}, 2),
+ TestCase{{-0x1.8000000000001p1, 0x1p-53}}.withAll(
+ {-0x1.8000000000001p-1, 0x1p-55}, 2),
+
+ // Input: (2^1021+2^969, 2^968-2^915)
+ TestCase{{0x1.0000000000001p1021, 0x1.fffffffffffffp967}}.withAll(
+ {0x1.0000000000001p-1, 0x1.fffffffffffffp-55}, 1022),
+ TestCase{{-0x1.0000000000001p1021, -0x1.fffffffffffffp967}}.withAll(
+ {-0x1.0000000000001p-1, -0x1.fffffffffffffp-55}, 1022),
+
+ // Input: (2^1023, -2^-1)
+ TestCase{{0x1p+1023, -0x1p-1}}.withAll({0x1p0, -0x1p-1024}, 1023),
+ TestCase{{-0x1p+1023, 0x1p-1}}.withAll({-0x1p0, 0x1p-1024}, 1023),
+
+ // Input: (2^1023, -2^-51)
+ TestCase{{0x1p+1023, -0x1p-51}}.withAll({0x1p0, -0x1p-1074}, 1023),
+ TestCase{{-0x1p+1023, 0x1p-51}}.withAll({-0x1p0, 0x1p-1074}, 1023),
+
+ // Input: (2^1023, -2^-52)
+ TestCase{{0x1p+1023, -0x1p-52}}
+ .withAll({0x1p-1, -0x0p0}, 1024)
+ .with(RDN, {0x1p0, -0x1p-1074}, 1023)
+ .with(RTZ, {0x1p0, -0x1p-1074}, 1023),
+ TestCase{{-0x1p+1023, 0x1p-52}}
+ .withAll({-0x1p-1, 0x0p0}, 1024)
+ .with(RUP, {-0x1p0, 0x1p-1074}, 1023)
+ .with(RTZ, {-0x1p0, 0x1p-1074}, 1023),
+
+ // Input: (2^1023, 2^-1074)
+ TestCase{{0x1p+1023, 0x1p-1074}}
+ .withAll({0x1p-1, 0x0p+0}, 1024)
+ .with(RUP, {0x1p-1, 0x1p-1074}, 1024),
+ TestCase{{-0x1p+1023, -0x1p-1074}}
+ .withAll({-0x1p-1, -0x0p+0}, 1024)
+ .with(RDN, {-0x1p-1, -0x1p-1074}, 1024),
+
+ // Input: (2^1024-2^971, 2^970-2^918)
+ TestCase{{0x1.fffffffffffffp+1023, 0x1.ffffffffffffep+969}}.withAll(
+ {0x1.fffffffffffffp-1, 0x1.ffffffffffffep-55}, 1024),
+ TestCase{{-0x1.fffffffffffffp+1023, -0x1.ffffffffffffep+969}}.withAll(
+ {-0x1.fffffffffffffp-1, -0x1.ffffffffffffep-55}, 1024),
+ };
+ return FrexpTestCases;
+}
+} // namespace PPCDoubleDoubleFrexpTestDetails
+
+class PPCDoubleDoubleFrexpValueTest
+ : public testing::Test,
+ public ::testing::WithParamInterface<
+ PPCDoubleDoubleFrexpTestDetails::TestCase> {};
+
+INSTANTIATE_TEST_SUITE_P(
+ PPCDoubleDoubleFrexpValueParamTests, PPCDoubleDoubleFrexpValueTest,
+ ::testing::ValuesIn(PPCDoubleDoubleFrexpTestDetails::testCases()));
+
+TEST_P(PPCDoubleDoubleFrexpValueTest, PPCDoubleDoubleFrexp) {
+ const PPCDoubleDoubleFrexpTestDetails::TestCase Params = GetParam();
+ const APFloat Input = makeDoubleAPFloat(Params.Input);
+ auto RmToIdx = [](APFloat::roundingMode RM) {
+ return static_cast<std::underlying_type_t<APFloat::roundingMode>>(RM);
+ };
+ // First, make sure our expected results are consistent with each other before
+ // bothering to test the implementation.
+ if (Input.isFinite()) {
+ // Make sure the input is canonical.
+ EXPECT_EQ(APFloat{Params.Input.Hi},
+ APFloat{Params.Input.Hi} + APFloat{Params.Input.Lo})
+ << Params.Input.Hi << " + " << Params.Input.Lo;
+
+ const auto Dn = Params.Rounded[RmToIdx(APFloat::rmTowardNegative)];
+ const auto Up = Params.Rounded[RmToIdx(APFloat::rmTowardPositive)];
+ const auto Tz = Params.Rounded[RmToIdx(APFloat::rmTowardZero)];
+ const auto Ne = Params.Rounded[RmToIdx(APFloat::rmNearestTiesToEven)];
+ const auto Na = Params.Rounded[RmToIdx(APFloat::rmNearestTiesToAway)];
+
+ // The rdn result must be no larger than the rup result.
+ const APFloat::cmpResult DnVsUp = compare(Dn, Up);
+ EXPECT_TRUE(DnVsUp == APFloat::cmpLessThan || DnVsUp == APFloat::cmpEqual);
+
+ for (size_t I = 0, E = std::size(Params.Rounded); I != E; ++I) {
+ const APFloat RoundedFraction =
+ makeDoubleAPFloat(Params.Rounded[I].Fraction);
+ // All possible results should be bracketed by [Dn, Up].
+ const APFloat::cmpResult VsDn = compare(Params.Rounded[I], Dn);
+ EXPECT_TRUE(VsDn == APFloat::cmpGreaterThan || VsDn == APFloat::cmpEqual);
+ const APFloat::cmpResult VsUp = compare(Params.Rounded[I], Up);
+ EXPECT_TRUE(VsUp == APFloat::cmpLessThan || VsUp == APFloat::cmpEqual);
+ // A rounding result is either equal to the rup or rdn result.
+ EXPECT_TRUE(VsUp == APFloat::cmpEqual || VsDn == APFloat::cmpEqual);
+ // frexp returns a result whose magnitude is in in [.5, 1) so its exponent
+ // should be -1.
+ if (!RoundedFraction.isZero())
+ EXPECT_EQ(ilogb(RoundedFraction), -1)
+ << static_cast<APFloat::roundingMode>(I);
+ // Decomposition preserves sign.
+ EXPECT_EQ(RoundedFraction.isNegative(), Input.isNegative());
+ // A rounding result must be canonical.
+ EXPECT_EQ(APFloat{Params.Rounded[I].Fraction.Hi},
+ APFloat{Params.Rounded[I].Fraction.Hi} +
+ APFloat{Params.Rounded[I].Fraction.Lo})
+ << Params.Rounded[I].Fraction.Hi << " + "
+ << Params.Rounded[I].Fraction.Lo;
+ }
+
+ // The rtz result must be either rup or rdn depending on the sign.
+ if (Input.isNegative()) {
+ const APFloat::cmpResult TzVsUp = compare(Tz, Up);
+ EXPECT_EQ(TzVsUp, APFloat::cmpEqual);
+ } else {
+ const APFloat::cmpResult TzVsDn = compare(Tz, Dn);
+ EXPECT_EQ(TzVsDn, APFloat::cmpEqual);
+ }
+
+ // The recomposed up should be at least as big as the input.
+ const APFloat RecomposedUp =
+ scalbn(makeDoubleAPFloat(Up.Fraction), Up.Exponent,
+ APFloat::rmNearestTiesToEven);
+ EXPECT_TRUE(RecomposedUp >= Input);
+ // The recomposed down can't be larger than the input.
+ const APFloat RecomposedDn =
+ scalbn(makeDoubleAPFloat(Dn.Fraction), Dn.Exponent,
+ APFloat::rmNearestTiesToEven);
+ EXPECT_TRUE(RecomposedDn <= Input);
+ // The recomposed tz must have a smaller magnitude.
+ const APFloat RecomposedTz =
+ scalbn(makeDoubleAPFloat(Tz.Fraction), Tz.Exponent,
+ APFloat::rmNearestTiesToEven);
+ EXPECT_TRUE(abs(RecomposedTz) <= abs(Input));
+ // Either both or neither of the recomposed round-to-nearest results are
+ // equal to the input.
+ const APFloat RecomposedNe =
+ scalbn(makeDoubleAPFloat(Ne.Fraction), Ne.Exponent,
+ APFloat::rmNearestTiesToEven);
+ const APFloat RecomposedNa =
+ scalbn(makeDoubleAPFloat(Na.Fraction), Na.Exponent,
+ APFloat::rmNearestTiesToEven);
+ EXPECT_EQ(RecomposedNe == Input, RecomposedNa == Input);
+ // Either the ne result equals the na result or the na result has a bigger
+ // magnitude.
+ const APFloat::cmpResult NeVsNa =
+ abs(RecomposedNe).compare(abs(RecomposedNa));
+ EXPECT_TRUE(NeVsNa == APFloat::cmpLessThan || NeVsNa == APFloat::cmpEqual);
+ // ne and na may only disagree if they broke a tie differently.
+ if (NeVsNa == APFloat::cmpLessThan) {
+ // ne's magnitude should be lower than input.
+ const APFloat::cmpResult NeVsInput =
+ abs(RecomposedNe).compare(abs(Input));
+ EXPECT_EQ(NeVsInput, APFloat::cmpLessThan);
+ // na's magnitude should be greater than input.
+ const APFloat::cmpResult NaVsInput =
+ abs(RecomposedNa).compare(abs(Input));
+ EXPECT_EQ(NaVsInput, APFloat::cmpGreaterThan);
+ }
+ // If up or down perfectly reconstructs the input, the round-to-nearest
+ // results should too.
+ if (RecomposedUp == Input || RecomposedDn == Input) {
+ EXPECT_EQ(RecomposedNe, Input);
+ EXPECT_EQ(RecomposedNa, Input);
+ }
+ }
+
+ for (size_t I = 0, E = std::size(Params.Rounded); I != E; ++I) {
+ const auto RM = static_cast<APFloat::roundingMode>(I);
+ const auto &Expected = Params.Rounded[I];
+ const APFloat ExpectedFraction = makeDoubleAPFloat(Expected.Fraction);
+
+ int ActualExponent;
+ const APFloat ActualFraction = frexp(Input, ActualExponent, RM);
+ if (ExpectedFraction.isNaN())
+ EXPECT_TRUE(ActualFraction.isNaN());
+ else
+ EXPECT_EQ(ActualFraction.compare(ExpectedFraction), APFloat::cmpEqual)
+ << ActualFraction << " vs " << ExpectedFraction << " for input "
+ << Params.Input.Hi << " + " << Params.Input.Lo << " RM " << RM;
+ EXPECT_EQ(ActualExponent, Expected.Exponent)
+ << "for input " << Params.Input.Hi << " + " << Params.Input.Lo
+ << " RM " << RM;
+ }
}
TEST(APFloatTest, PPCDoubleDoubleNext) {
@@ -5551,13 +6875,9 @@ TEST(APFloatTest, PPCDoubleDoubleNext) {
return X;
};
- auto Zero = [] {
- return APFloat::getZero(APFloat::IEEEdouble());
- };
+ auto Zero = [] { return APFloat::getZero(APFloat::IEEEdouble()); };
- auto One = [] {
- return APFloat::getOne(APFloat::IEEEdouble());
- };
+ auto One = [] { return APFloat::getOne(APFloat::IEEEdouble()); };
// 0x1p-1074
auto MinSubnormal = [] {
@@ -5574,24 +6894,6 @@ TEST(APFloatTest, PPCDoubleDoubleNext) {
// 2^-53
auto EpsNeg = [&] { return scalbn(Eps(), -1, APFloat::rmNearestTiesToEven); };
- auto MakeDoubleAPFloat = [](auto Hi, auto Lo) {
- APFloat HiFloat{APFloat::IEEEdouble(), APFloat::uninitialized};
- if constexpr (std::is_same_v<decltype(Hi), APFloat>) {
- HiFloat = Hi;
- } else {
- HiFloat = {APFloat::IEEEdouble(), Hi};
- }
-
- APFloat LoFloat{APFloat::IEEEdouble(), APFloat::uninitialized};
- if constexpr (std::is_same_v<decltype(Lo), APFloat>) {
- LoFloat = Lo;
- } else {
- LoFloat = {APFloat::IEEEdouble(), Lo};
- }
-
- APInt Bits = LoFloat.bitcastToAPInt().concat(HiFloat.bitcastToAPInt());
- return APFloat(APFloat::PPCDoubleDouble(), Bits);
- };
APFloat Test(APFloat::PPCDoubleDouble(), APFloat::uninitialized);
APFloat Expected(APFloat::PPCDoubleDouble(), APFloat::uninitialized);
@@ -5719,55 +7021,55 @@ TEST(APFloatTest, PPCDoubleDoubleNext) {
// 2b. |hi| >= 2*DBL_MIN_NORMAL (DD precision > D precision)
// Test at hi = 1.0, lo = 0.
- Test = MakeDoubleAPFloat(One(), Zero());
- Expected = MakeDoubleAPFloat(One(), MinSubnormal());
+ Test = makeDoubleAPFloat(One(), Zero());
+ Expected = makeDoubleAPFloat(One(), MinSubnormal());
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_TRUE(Test.bitwiseIsEqual(Expected));
// Test at hi = -1.0. delta = 2^-1074 (positive, moving towards +Inf).
- Test = MakeDoubleAPFloat(-One(), Zero());
- Expected = MakeDoubleAPFloat(-One(), MinSubnormal());
+ Test = makeDoubleAPFloat(-One(), Zero());
+ Expected = makeDoubleAPFloat(-One(), MinSubnormal());
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_TRUE(Test.bitwiseIsEqual(Expected));
// Testing the boundary where calculated delta equals DBL_TRUE_MIN.
// Requires ilogb(hi) = E = -968.
// delta = 2^(-968 - 106) = 2^-1074 = DBL_TRUE_MIN.
- Test = MakeDoubleAPFloat("0x1p-968", Zero());
- Expected = MakeDoubleAPFloat("0x1p-968", MinSubnormal());
+ Test = makeDoubleAPFloat("0x1p-968", Zero());
+ Expected = makeDoubleAPFloat("0x1p-968", MinSubnormal());
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_TRUE(Test.bitwiseIsEqual(Expected));
// Testing below the boundary (E < -968). Delta clamps to DBL_TRUE_MIN.
- Test = MakeDoubleAPFloat("0x1p-969", Zero());
- Expected = MakeDoubleAPFloat("0x1p-969", MinSubnormal());
+ Test = makeDoubleAPFloat("0x1p-969", Zero());
+ Expected = makeDoubleAPFloat("0x1p-969", MinSubnormal());
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_TRUE(Test.bitwiseIsEqual(Expected));
// 3. Standard Increment (No rollover)
// hi=1.0, lo=2^-1074.
- Test = MakeDoubleAPFloat(One(), MinSubnormal());
- Expected = MakeDoubleAPFloat(One(), NextUp(MinSubnormal()));
+ Test = makeDoubleAPFloat(One(), MinSubnormal());
+ Expected = makeDoubleAPFloat(One(), NextUp(MinSubnormal()));
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_TRUE(Test.bitwiseIsEqual(Expected));
// Incrementing negative lo.
- Test = MakeDoubleAPFloat(One(), -MinSubnormal());
- Expected = MakeDoubleAPFloat(One(), Zero());
+ Test = makeDoubleAPFloat(One(), -MinSubnormal());
+ Expected = makeDoubleAPFloat(One(), Zero());
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_EQ(Test.compare(Expected), APFloat::cmpEqual);
// Crossing lo=0.
- Test = MakeDoubleAPFloat(One(), -MinSubnormal());
- Expected = MakeDoubleAPFloat(One(), Zero());
+ Test = makeDoubleAPFloat(One(), -MinSubnormal());
+ Expected = makeDoubleAPFloat(One(), Zero());
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_EQ(Test.compare(Expected), APFloat::cmpEqual);
// 4. Rollover Cases around 1.0 (Positive hi)
// hi=1.0, lo=nextDown(2^-53).
- Test = MakeDoubleAPFloat(One(), NextDown(EpsNeg()));
+ Test = makeDoubleAPFloat(One(), NextDown(EpsNeg()));
EXPECT_FALSE(Test.isDenormal());
- Expected = MakeDoubleAPFloat(One(), EpsNeg());
+ Expected = makeDoubleAPFloat(One(), EpsNeg());
EXPECT_FALSE(Test.isDenormal());
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_TRUE(Test.bitwiseIsEqual(Expected));
@@ -5778,17 +7080,17 @@ TEST(APFloatTest, PPCDoubleDoubleNext) {
// Can't naively TwoSum(0x1p+0, nextUp(0x1p-53)):
// It gives {nextUp(0x1p+0), nextUp(nextUp(-0x1p-53))} but the next
// number should be {nextUp(0x1p+0), nextUp(-0x1p-53)}.
- Test = MakeDoubleAPFloat(One(), EpsNeg());
+ Test = makeDoubleAPFloat(One(), EpsNeg());
EXPECT_FALSE(Test.isDenormal());
- Expected = MakeDoubleAPFloat(NextUp(One()), NextUp(-EpsNeg()));
+ Expected = makeDoubleAPFloat(NextUp(One()), NextUp(-EpsNeg()));
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_TRUE(Test.bitwiseIsEqual(Expected));
EXPECT_FALSE(Test.isDenormal());
// hi = nextDown(1), lo = nextDown(0x1p-54)
- Test = MakeDoubleAPFloat(NextDown(One()), NextDown(APFloat(0x1p-54)));
+ Test = makeDoubleAPFloat(NextDown(One()), NextDown(APFloat(0x1p-54)));
EXPECT_FALSE(Test.isDenormal());
- Expected = MakeDoubleAPFloat(One(), APFloat(-0x1p-54));
+ Expected = makeDoubleAPFloat(One(), APFloat(-0x1p-54));
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_TRUE(Test.bitwiseIsEqual(Expected));
EXPECT_FALSE(Test.isDenormal());
@@ -5796,31 +7098,274 @@ TEST(APFloatTest, PPCDoubleDoubleNext) {
// 5. Negative Rollover (Moving towards Zero / +Inf)
// hi = -1, lo = nextDown(0x1p-54)
- Test = MakeDoubleAPFloat(APFloat(-1.0), NextDown(APFloat(0x1p-54)));
+ Test = makeDoubleAPFloat(APFloat(-1.0), NextDown(APFloat(0x1p-54)));
EXPECT_FALSE(Test.isDenormal());
- Expected = MakeDoubleAPFloat(APFloat(-1.0), APFloat(0x1p-54));
+ Expected = makeDoubleAPFloat(APFloat(-1.0), APFloat(0x1p-54));
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_TRUE(Test.bitwiseIsEqual(Expected));
EXPECT_FALSE(Test.isDenormal());
// hi = -1, lo = 0x1p-54
- Test = MakeDoubleAPFloat(APFloat(-1.0), APFloat(0x1p-54));
+ Test = makeDoubleAPFloat(APFloat(-1.0), APFloat(0x1p-54));
EXPECT_FALSE(Test.isDenormal());
Expected =
- MakeDoubleAPFloat(NextUp(APFloat(-1.0)), NextUp(APFloat(-0x1p-54)));
+ makeDoubleAPFloat(NextUp(APFloat(-1.0)), NextUp(APFloat(-0x1p-54)));
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_TRUE(Test.bitwiseIsEqual(Expected));
EXPECT_FALSE(Test.isDenormal());
// 6. Rollover across Power of 2 boundary (Exponent change)
- Test = MakeDoubleAPFloat(NextDown(APFloat(2.0)), NextDown(EpsNeg()));
+ Test = makeDoubleAPFloat(NextDown(APFloat(2.0)), NextDown(EpsNeg()));
EXPECT_FALSE(Test.isDenormal());
- Expected = MakeDoubleAPFloat(APFloat(2.0), -EpsNeg());
+ Expected = makeDoubleAPFloat(APFloat(2.0), -EpsNeg());
EXPECT_EQ(Test.next(false), APFloat::opOK);
EXPECT_TRUE(Test.bitwiseIsEqual(Expected));
EXPECT_FALSE(Test.isDenormal());
}
+TEST(APFloatTest, PPCDoubleDoubleConvertFromAPIntInexact) {
+ // Create an integer which would not be exactly representable in
+ // PPCDoubleDoubleLegacy.
+ for (bool IsSigned : {false, true}) {
+ const unsigned BitWidth =
+ APFloat::semanticsPrecision(APFloat::IEEEdouble()) * 3 +
+ (IsSigned ? 1 : 0);
+
+ for (bool Negative :
+ IsSigned ? std::vector{false, true} : std::vector{false}) {
+ APInt Huge = APInt{BitWidth, 0};
+ // Set the highest bit without making Huge negative..
+ Huge.setBit(BitWidth - (IsSigned ? 2 : 1));
+ // Set the low bit.
+ Huge.setBit(0);
+ if (Negative)
+ Huge.negate();
+ for (const APFloat::roundingMode RM :
+ {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative,
+ APFloat::rmTowardPositive, APFloat::rmTowardZero,
+ APFloat::rmNearestTiesToEven}) {
+ APFloat F{APFloat::PPCDoubleDouble()};
+ const APFloat::opStatus ConvertFromStatus =
+ F.convertFromAPInt(Huge, /*IsSigned=*/IsSigned, RM);
+ EXPECT_EQ(ConvertFromStatus, APFloat::opOK);
+
+ bool IsExact;
+ APSInt ResultInt{Huge.getBitWidth(), /*isUnsigned=*/!IsSigned};
+ const APFloat::opStatus ConvertToStatus =
+ F.convertToInteger(ResultInt, APFloat::rmTowardZero, &IsExact);
+
+ EXPECT_TRUE(IsExact) << "RM: " << RM;
+ EXPECT_TRUE(ResultInt.eq(Huge)) << ResultInt << " vs " << Huge << "\n";
+ EXPECT_EQ(ConvertToStatus, APFloat::opOK);
+ }
+ }
+ }
+}
+
+TEST(APFloatTest, PPCDoubleDoubleConvertFromAPIntBoundary) {
+ const unsigned Binary64Precision =
+ APFloat::semanticsPrecision(APFloat::IEEEdouble());
+ APSInt Boundary =
+ APSInt::getMaxValue(Binary64Precision + 1, /*Unsigned=*/true);
+ for (const APFloat::roundingMode RM :
+ {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative,
+ APFloat::rmTowardPositive, APFloat::rmTowardZero,
+ APFloat::rmNearestTiesToEven}) {
+ const APFloat Exact = makeDoubleAPFloat(0x1p54, -0x1p0);
+ APFloat F{APFloat::PPCDoubleDouble()};
+ const APFloat::opStatus ConvertFromStatus =
+ F.convertFromAPInt(Boundary, /*IsSigned=*/false, RM);
+ EXPECT_EQ(ConvertFromStatus, APFloat::opOK);
+ EXPECT_EQ(F, Exact);
+ }
+
+ Boundary = APSInt{APInt::getHighBitsSet(/*numBits=*/128,
+ /*hiBitsSet=*/Binary64Precision + 1),
+ /*isUnsigned=*/true};
+ for (const APFloat::roundingMode RM :
+ {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative,
+ APFloat::rmTowardPositive, APFloat::rmTowardZero,
+ APFloat::rmNearestTiesToEven}) {
+ const APFloat Exact = makeDoubleAPFloat(0x1p128, -0x1p74);
+ APFloat F{APFloat::PPCDoubleDouble()};
+ const APFloat::opStatus ConvertFromStatus =
+ F.convertFromAPInt(Boundary, /*IsSigned=*/false, RM);
+ EXPECT_EQ(ConvertFromStatus, APFloat::opOK);
+ EXPECT_EQ(F, Exact);
+ }
+}
+
+TEST(APFloatTest, PPCDoubleDoubleConvertFromAPIntEnormous) {
+ APFloat Largest = APFloat::getLargest(APFloat::PPCDoubleDouble());
+ int Exponent = ilogb(Largest);
+ unsigned BitWidth = Exponent + 1;
+ APSInt HugeInt{BitWidth, /*isUnsigned=*/true};
+ bool IsExact;
+ APFloat::opStatus Status =
+ Largest.convertToInteger(HugeInt, APFloat::rmTowardPositive, &IsExact);
+ ASSERT_EQ(Status, APFloat::opOK);
+ ASSERT_TRUE(IsExact);
+
+ for (const APFloat::roundingMode RM :
+ {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative,
+ APFloat::rmTowardPositive, APFloat::rmTowardZero,
+ APFloat::rmNearestTiesToEven}) {
+ APFloat F{APFloat::PPCDoubleDouble()};
+ const APFloat::opStatus ConvertFromStatus =
+ F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM);
+ EXPECT_EQ(ConvertFromStatus, APFloat::opOK);
+ EXPECT_EQ(F, Largest);
+ }
+
+ const unsigned MaxExponent =
+ APFloat::semanticsMaxExponent(APFloat::IEEEdouble());
+ const unsigned Binary64Precision =
+ APFloat::semanticsPrecision(APFloat::IEEEdouble());
+ const unsigned UlpOfLargest = MaxExponent - (2 * Binary64Precision);
+ const unsigned HalfUlpOfLargest = UlpOfLargest - 1;
+
+ // Add just under a half-ulp. This should never overflow for
+ // round-ties-to-nearest modes.
+ HugeInt.setLowBits(HalfUlpOfLargest);
+ for (const APFloat::roundingMode RM :
+ {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative,
+ APFloat::rmTowardPositive, APFloat::rmTowardZero,
+ APFloat::rmNearestTiesToEven}) {
+ APFloat F{APFloat::PPCDoubleDouble()};
+ const APFloat::opStatus ConvertFromStatus =
+ F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM);
+ if (RM == APFloat::rmTowardPositive) {
+ EXPECT_TRUE(F.isPosInfinity()) << F;
+ EXPECT_EQ(ConvertFromStatus, APFloat::opInexact | APFloat::opOverflow);
+ } else {
+ EXPECT_EQ(F, Largest);
+ EXPECT_EQ(ConvertFromStatus, APFloat::opInexact);
+ }
+ }
+
+ // Now test adding a half-ulp. This should cause overflow for ties-to-away.
+ // ties-to-even will not overflow if the max finite value has a clear low bit.
+ ++HugeInt;
+ for (const APFloat::roundingMode RM :
+ {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative,
+ APFloat::rmTowardPositive, APFloat::rmTowardZero,
+ APFloat::rmNearestTiesToEven}) {
+ APFloat F{APFloat::PPCDoubleDouble()};
+ const APFloat::opStatus ConvertFromStatus =
+ F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM);
+ const bool Overflow =
+ RM == APFloat::rmTowardPositive || RM == APFloat::rmNearestTiesToAway ||
+ (RM == APFloat::rmNearestTiesToEven && HugeInt[UlpOfLargest]);
+ if (Overflow) {
+ EXPECT_TRUE(F.isPosInfinity()) << F;
+ EXPECT_EQ(ConvertFromStatus, APFloat::opInexact | APFloat::opOverflow);
+ } else {
+ EXPECT_EQ(F, Largest);
+ EXPECT_EQ(ConvertFromStatus, APFloat::opInexact);
+ }
+ }
+
+ // Now test adding just over a half-ulp. This should break all ties.
+ ++HugeInt;
+ for (const APFloat::roundingMode RM :
+ {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative,
+ APFloat::rmTowardPositive, APFloat::rmTowardZero,
+ APFloat::rmNearestTiesToEven}) {
+ APFloat F{APFloat::PPCDoubleDouble()};
+ const APFloat::opStatus ConvertFromStatus =
+ F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM);
+ const bool Overflow = RM == APFloat::rmTowardPositive ||
+ RM == APFloat::rmNearestTiesToAway ||
+ RM == APFloat::rmNearestTiesToEven;
+ if (Overflow) {
+ EXPECT_TRUE(F.isPosInfinity()) << F;
+ EXPECT_EQ(ConvertFromStatus, APFloat::opInexact | APFloat::opOverflow);
+ } else {
+ EXPECT_EQ(F, Largest);
+ EXPECT_EQ(ConvertFromStatus, APFloat::opInexact);
+ }
+ }
+
+ HugeInt.setAllBits();
+ for (const APFloat::roundingMode RM :
+ {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative,
+ APFloat::rmTowardPositive, APFloat::rmTowardZero,
+ APFloat::rmNearestTiesToEven}) {
+ APFloat F{APFloat::PPCDoubleDouble()};
+ const APFloat::opStatus ConvertFromStatus =
+ F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM);
+ const bool Overflow = RM == APFloat::rmTowardPositive ||
+ RM == APFloat::rmNearestTiesToAway ||
+ RM == APFloat::rmNearestTiesToEven;
+ if (Overflow) {
+ EXPECT_TRUE(F.isPosInfinity()) << F;
+ EXPECT_EQ(ConvertFromStatus, APFloat::opInexact | APFloat::opOverflow);
+ } else {
+ EXPECT_EQ(F, Largest);
+ EXPECT_EQ(ConvertFromStatus, APFloat::opInexact);
+ }
+ }
+
+ HugeInt.clearAllBits();
+ HugeInt.setBit(2 * Binary64Precision + 1);
+ HugeInt.setLowBits(Binary64Precision + 1);
+ for (const APFloat::roundingMode RM :
+ {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative,
+ APFloat::rmTowardPositive, APFloat::rmTowardZero,
+ APFloat::rmNearestTiesToEven}) {
+ const APFloat RoundUp = makeDoubleAPFloat(0x1p107, 0x1p54);
+ const APFloat RoundDown = makeDoubleAPFloat(0x1p107, 0x1.fffffffffffffp53);
+ APFloat F{APFloat::PPCDoubleDouble()};
+ const APFloat::opStatus ConvertFromStatus =
+ F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM);
+ EXPECT_EQ(ConvertFromStatus, APFloat::opInexact);
+
+ if (RM == APFloat::rmNearestTiesToEven ||
+ RM == APFloat::rmNearestTiesToAway || RM == APFloat::rmTowardPositive)
+ EXPECT_EQ(F, RoundUp);
+ else
+ EXPECT_EQ(F, RoundDown);
+ }
+
+ ++HugeInt;
+ // 162259276829213381405976519770112 can be represented exactly.
+ for (const APFloat::roundingMode RM :
+ {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative,
+ APFloat::rmTowardPositive, APFloat::rmTowardZero,
+ APFloat::rmNearestTiesToEven}) {
+ const APFloat Exact = makeDoubleAPFloat(0x1p107, 0x1p54);
+ APFloat F{APFloat::PPCDoubleDouble()};
+ const APFloat::opStatus ConvertFromStatus =
+ F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM);
+ EXPECT_EQ(ConvertFromStatus, APFloat::opOK);
+ EXPECT_EQ(F, Exact);
+ }
+
+ ++HugeInt;
+ // 162259276829213381405976519770113 rounds to either:
+ // 162259276829213381405976519770112
+ // 162259276829213381405976519770114
+ for (const APFloat::roundingMode RM :
+ {APFloat::rmNearestTiesToAway, APFloat::rmTowardNegative,
+ APFloat::rmTowardPositive, APFloat::rmTowardZero,
+ APFloat::rmNearestTiesToEven}) {
+ const APFloat RoundUp =
+ makeDoubleAPFloat(0x1.0000000000001p107, -0x1.fffffffffffffp53);
+ const APFloat RoundDown = makeDoubleAPFloat(0x1p107, 0x1p54);
+ EXPECT_LT(RoundDown, RoundUp);
+
+ APFloat F{APFloat::PPCDoubleDouble()};
+ const APFloat::opStatus ConvertFromStatus =
+ F.convertFromAPInt(HugeInt, /*IsSigned=*/false, RM);
+ EXPECT_EQ(ConvertFromStatus, APFloat::opInexact);
+
+ if (RM == APFloat::rmNearestTiesToAway || RM == APFloat::rmTowardPositive)
+ EXPECT_EQ(F, RoundUp);
+ else
+ EXPECT_EQ(F, RoundDown);
+ }
+}
+
TEST(APFloatTest, x87Largest) {
APFloat MaxX87Val = APFloat::getLargest(APFloat::x87DoubleExtended());
EXPECT_TRUE(MaxX87Val.isLargest());
@@ -7812,13 +9357,8 @@ TEST(APFloatTest, getExactLog2) {
continue;
APFloat One(Semantics, "1.0");
-
- if (I == APFloat::S_PPCDoubleDouble) {
- // Not implemented
- EXPECT_EQ(INT_MIN, One.getExactLog2());
- EXPECT_EQ(INT_MIN, One.getExactLog2Abs());
- continue;
- }
+ APFloat Smallest = APFloat::getSmallest(Semantics);
+ APFloat Largest = APFloat::getLargest(Semantics);
int MinExp = APFloat::semanticsMinExponent(Semantics);
int MaxExp = APFloat::semanticsMaxExponent(Semantics);
@@ -7865,16 +9405,15 @@ TEST(APFloatTest, getExactLog2) {
EXPECT_EQ(INT_MIN, APFloat::getNaN(Semantics, true).getExactLog2Abs());
}
- EXPECT_EQ(INT_MIN,
- scalbn(One, MinExp - Precision - 1, APFloat::rmNearestTiesToEven)
- .getExactLog2());
- EXPECT_EQ(INT_MIN,
- scalbn(One, MinExp - Precision, APFloat::rmNearestTiesToEven)
- .getExactLog2());
-
EXPECT_EQ(
INT_MIN,
- scalbn(One, MaxExp + 1, APFloat::rmNearestTiesToEven).getExactLog2());
+ scalbn(Smallest, -2, APFloat::rmNearestTiesToEven).getExactLog2());
+ EXPECT_EQ(
+ INT_MIN,
+ scalbn(Smallest, -1, APFloat::rmNearestTiesToEven).getExactLog2());
+
+ EXPECT_EQ(INT_MIN,
+ scalbn(Largest, 1, APFloat::rmNearestTiesToEven).getExactLog2());
for (int i = MinExp - Precision + 1; i <= MaxExp; ++i) {
EXPECT_EQ(i, scalbn(One, i, APFloat::rmNearestTiesToEven).getExactLog2());
diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp
index 4741c7b..116693c 100644
--- a/llvm/unittests/ADT/APIntTest.cpp
+++ b/llvm/unittests/ADT/APIntTest.cpp
@@ -3103,6 +3103,53 @@ TEST(APIntOpsTest, Mulh) {
EXPECT_EQ(APInt(128, "FFEB498812C66C68D4552DB89B8EBF8F", 16), i128Res);
}
+TEST(APIntOpsTest, muli) {
+ APInt u32a(32, 0x0001'E235);
+ APInt u32b(32, 0xF623'55AD);
+ EXPECT_EQ(0x0001'CFA1'7CA0'76D1, APIntOps::muluExtended(u32a, u32b));
+
+ APInt u64a(64, 0x1234'5678'90AB'CDEF);
+ APInt u64b(64, 0xFEDC'BA09'8765'4321);
+ EXPECT_EQ(APInt(128, "121FA000A3723A57C24A442FE55618CF", 16),
+ APIntOps::muluExtended(u64a, u64b));
+
+ APInt u128a(128, "1234567890ABCDEF1234567890ABCDEF", 16);
+ APInt u128b(128, "FEDCBA0987654321FEDCBA0987654321", 16);
+ EXPECT_EQ(
+ APInt(256,
+ "121FA000A3723A57E68984312C3A8D7E96B428606E1E6BF5C24A442FE55618CF",
+ 16),
+ APIntOps::muluExtended(u128a, u128b));
+
+ APInt s32a(32, 0x1234'5678);
+ APInt s32b(32, 0x10AB'CDEF);
+ APInt s32c(32, 0xFEDC'BA09);
+ EXPECT_EQ(0x012F'7D02'2A42'D208, APIntOps::mulsExtended(s32a, s32b));
+ EXPECT_EQ(0xFFEB'4988'09CA'3A38, APIntOps::mulsExtended(s32a, s32c));
+
+ APInt s64a(64, 0x1234'5678'90AB'CDEF);
+ APInt s64b(64, 0x1234'5678'90FE'DCBA);
+ APInt s64c(64, 0xFEDC'BA09'8765'4321);
+ EXPECT_EQ(APInt(128, "014B66DC328E10C1FB99704184EF03A6", 16),
+ APIntOps::mulsExtended(s64a, s64b));
+ EXPECT_EQ(APInt(128, "FFEB498812C66C68C24A442FE55618CF", 16),
+ APIntOps::mulsExtended(s64a, s64c));
+
+ APInt s128a(128, "1234567890ABCDEF1234567890ABCDEF", 16);
+ APInt s128b(128, "1234567890FEDCBA1234567890FEDCBA", 16);
+ APInt s128c(128, "FEDCBA0987654321FEDCBA0987654321", 16);
+ EXPECT_EQ(
+ APInt(256,
+ "014B66DC328E10C1FE303DF9EA0B2529F87E475F3C6C180DFB99704184EF03A6",
+ 16),
+ APIntOps::mulsExtended(s128a, s128b));
+ EXPECT_EQ(
+ APInt(256,
+ "FFEB498812C66C68D4552DB89B8EBF8F96B428606E1E6BF5C24A442FE55618CF",
+ 16),
+ APIntOps::mulsExtended(s128a, s128c));
+}
+
TEST(APIntTest, RoundingUDiv) {
for (uint64_t Ai = 1; Ai <= 255; Ai++) {
APInt A(8, Ai);
@@ -3697,4 +3744,82 @@ TEST(APIntTest, TryExt) {
ASSERT_EQ(42, APInt(128, -1).trySExtValue().value_or(42));
}
+TEST(APIntTest, Fshl) {
+ EXPECT_EQ(
+ APIntOps::fshl(APInt(8, 0), APInt(8, 255), APInt(8, 8)).getZExtValue(),
+ 0U);
+ EXPECT_EQ(
+ APIntOps::fshl(APInt(8, 255), APInt(8, 0), APInt(8, 8)).getZExtValue(),
+ 255U);
+ EXPECT_EQ(
+ APIntOps::fshl(APInt(8, 255), APInt(8, 0), APInt(8, 15)).getZExtValue(),
+ 128U);
+ EXPECT_EQ(
+ APIntOps::fshl(APInt(8, 15), APInt(8, 15), APInt(8, 11)).getZExtValue(),
+ 120U);
+ EXPECT_EQ(
+ APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 3)).getZExtValue(),
+ 16U);
+ EXPECT_EQ(
+ APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 1)).getZExtValue(),
+ APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 9)).getZExtValue());
+ EXPECT_EQ(
+ APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 7)).getZExtValue(),
+ APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 15)).getZExtValue());
+ EXPECT_EQ(APIntOps::fshl(APInt(32, 0, /*isSigned*/ true),
+ APInt(32, 2147483647, /*isSigned*/ true),
+ APInt(32, 32, /*isSigned*/ true))
+ .getSExtValue(),
+ 0);
+ EXPECT_EQ(APIntOps::fshl(APInt(64, 1, /*isSigned*/ true),
+ APInt(64, 2, /*isSigned*/ true),
+ APInt(64, 3, /*isSigned*/ true))
+ .getSExtValue(),
+ 8);
+ EXPECT_EQ(APIntOps::fshl(APInt(16, -2, /*isSigned*/ true),
+ APInt(16, -1, /*isSigned*/ true),
+ APInt(16, 3, /*isSigned*/ true))
+ .getSExtValue(),
+ -9);
+}
+
+TEST(APIntTest, Fshr) {
+ EXPECT_EQ(
+ APIntOps::fshr(APInt(8, 0), APInt(8, 255), APInt(8, 8)).getZExtValue(),
+ 255U);
+ EXPECT_EQ(
+ APIntOps::fshr(APInt(8, 255), APInt(8, 0), APInt(8, 8)).getZExtValue(),
+ 0U);
+ EXPECT_EQ(
+ APIntOps::fshr(APInt(8, 255), APInt(8, 0), APInt(8, 15)).getZExtValue(),
+ 254U);
+ EXPECT_EQ(
+ APIntOps::fshr(APInt(8, 15), APInt(8, 15), APInt(8, 11)).getZExtValue(),
+ 225U);
+ EXPECT_EQ(
+ APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 3)).getZExtValue(),
+ 32U);
+ EXPECT_EQ(
+ APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 1)).getZExtValue(),
+ APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 9)).getZExtValue());
+ EXPECT_EQ(
+ APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 7)).getZExtValue(),
+ APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 15)).getZExtValue());
+ EXPECT_EQ(APIntOps::fshr(APInt(64, 0, /*isSigned*/ true),
+ APInt(64, 9223372036854775807, /*isSigned*/ true),
+ APInt(64, 64, /*isSigned*/ true))
+ .getSExtValue(),
+ 9223372036854775807);
+ EXPECT_EQ(APIntOps::fshr(APInt(64, 1, /*isSigned*/ true),
+ APInt(64, 2, /*isSigned*/ true),
+ APInt(64, 3, /*isSigned*/ true))
+ .getSExtValue(),
+ 2305843009213693952);
+ EXPECT_EQ(APIntOps::fshr(APInt(16, -2, /*isSigned*/ true),
+ APInt(16, -1, /*isSigned*/ true),
+ APInt(16, 3, /*isSigned*/ true))
+ .getSExtValue(),
+ -8193);
+}
+
} // end anonymous namespace
diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp
index bdfbc85..785ab16 100644
--- a/llvm/unittests/ADT/DenseMapTest.cpp
+++ b/llvm/unittests/ADT/DenseMapTest.cpp
@@ -10,6 +10,7 @@
#include "CountCopyAndMove.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/DenseMapInfoVariant.h"
+#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringRef.h"
#include "gmock/gmock.h"
@@ -249,6 +250,25 @@ TYPED_TEST(DenseMapTest, CopyConstructorNotSmallTest) {
EXPECT_EQ(this->getValue(Key), copyMap[this->getKey(Key)]);
}
+// Test range constructors.
+TYPED_TEST(DenseMapTest, RangeConstructorTest) {
+ using KeyAndValue =
+ std::pair<typename TypeParam::key_type, typename TypeParam::mapped_type>;
+ KeyAndValue PlainArray[] = {{this->getKey(0), this->getValue(0)},
+ {this->getKey(1), this->getValue(1)}};
+
+ TypeParam MapFromRange(llvm::from_range, PlainArray);
+ EXPECT_EQ(2u, MapFromRange.size());
+ EXPECT_EQ(this->getValue(0), MapFromRange[this->getKey(0)]);
+ EXPECT_EQ(this->getValue(1), MapFromRange[this->getKey(1)]);
+
+ TypeParam MapFromInitList({{this->getKey(0), this->getValue(1)},
+ {this->getKey(1), this->getValue(2)}});
+ EXPECT_EQ(2u, MapFromInitList.size());
+ EXPECT_EQ(this->getValue(1), MapFromInitList[this->getKey(0)]);
+ EXPECT_EQ(this->getValue(2), MapFromInitList[this->getKey(1)]);
+}
+
// Test copying from a default-constructed map.
TYPED_TEST(DenseMapTest, CopyConstructorFromDefaultTest) {
TypeParam copyMap(this->Map);
@@ -367,9 +387,16 @@ TYPED_TEST(DenseMapTest, ConstIteratorTest) {
EXPECT_TRUE(cit == cit2);
}
+// TYPED_TEST below cycles through different types. We define UniversalSmallSet
+// here so that we'll use SmallSet or SmallPtrSet depending on whether the
+// element type is a pointer.
+template <typename T, unsigned N>
+using UniversalSmallSet =
+ std::conditional_t<std::is_pointer_v<T>, SmallPtrSet<T, N>, SmallSet<T, N>>;
+
TYPED_TEST(DenseMapTest, KeysValuesIterator) {
- SmallSet<typename TypeParam::key_type, 10> Keys;
- SmallSet<typename TypeParam::mapped_type, 10> Values;
+ UniversalSmallSet<typename TypeParam::key_type, 10> Keys;
+ UniversalSmallSet<typename TypeParam::mapped_type, 10> Values;
for (int I = 0; I < 10; ++I) {
auto K = this->getKey(I);
auto V = this->getValue(I);
@@ -378,8 +405,8 @@ TYPED_TEST(DenseMapTest, KeysValuesIterator) {
this->Map[K] = V;
}
- SmallSet<typename TypeParam::key_type, 10> ActualKeys;
- SmallSet<typename TypeParam::mapped_type, 10> ActualValues;
+ UniversalSmallSet<typename TypeParam::key_type, 10> ActualKeys;
+ UniversalSmallSet<typename TypeParam::mapped_type, 10> ActualValues;
for (auto K : this->Map.keys())
ActualKeys.insert(K);
for (auto V : this->Map.values())
@@ -390,8 +417,8 @@ TYPED_TEST(DenseMapTest, KeysValuesIterator) {
}
TYPED_TEST(DenseMapTest, ConstKeysValuesIterator) {
- SmallSet<typename TypeParam::key_type, 10> Keys;
- SmallSet<typename TypeParam::mapped_type, 10> Values;
+ UniversalSmallSet<typename TypeParam::key_type, 10> Keys;
+ UniversalSmallSet<typename TypeParam::mapped_type, 10> Values;
for (int I = 0; I < 10; ++I) {
auto K = this->getKey(I);
auto V = this->getValue(I);
@@ -401,8 +428,8 @@ TYPED_TEST(DenseMapTest, ConstKeysValuesIterator) {
}
const TypeParam &ConstMap = this->Map;
- SmallSet<typename TypeParam::key_type, 10> ActualKeys;
- SmallSet<typename TypeParam::mapped_type, 10> ActualValues;
+ UniversalSmallSet<typename TypeParam::key_type, 10> ActualKeys;
+ UniversalSmallSet<typename TypeParam::mapped_type, 10> ActualValues;
for (auto K : ConstMap.keys())
ActualKeys.insert(K);
for (auto V : ConstMap.values())
@@ -726,6 +753,15 @@ TEST(DenseMapCustomTest, FindAsTest) {
EXPECT_TRUE(map.find_as("d") == map.end());
}
+TEST(DenseMapCustomTest, SmallDenseMapFromRange) {
+ std::pair<int, StringRef> PlainArray[] = {{0, "0"}, {1, "1"}, {2, "2"}};
+ SmallDenseMap<int, StringRef> M(llvm::from_range, PlainArray);
+ EXPECT_EQ(3u, M.size());
+ using testing::Pair;
+ EXPECT_THAT(M, testing::UnorderedElementsAre(Pair(0, "0"), Pair(1, "1"),
+ Pair(2, "2")));
+}
+
TEST(DenseMapCustomTest, SmallDenseMapInitializerList) {
SmallDenseMap<int, int> M = {{0, 0}, {0, 1}, {1, 2}};
EXPECT_EQ(2u, M.size());
diff --git a/llvm/unittests/ADT/SmallPtrSetTest.cpp b/llvm/unittests/ADT/SmallPtrSetTest.cpp
index 4ea7403..a627091 100644
--- a/llvm/unittests/ADT/SmallPtrSetTest.cpp
+++ b/llvm/unittests/ADT/SmallPtrSetTest.cpp
@@ -475,4 +475,8 @@ TEST(SmallPtrSetTest, Reserve) {
EXPECT_EQ(Set.capacity(), 128u);
EXPECT_EQ(Set.size(), 6u);
EXPECT_THAT(Set, UnorderedElementsAre(&Vals[0], &Vals[1], &Vals[2], &Vals[3], &Vals[4], &Vals[5]));
+
+ // Reserving 192 should result in 256 buckets.
+ Set.reserve(192);
+ EXPECT_EQ(Set.capacity(), 256u);
}
diff --git a/llvm/unittests/ADT/StringRefTest.cpp b/llvm/unittests/ADT/StringRefTest.cpp
index ec9cdc1..d5f8dc4 100644
--- a/llvm/unittests/ADT/StringRefTest.cpp
+++ b/llvm/unittests/ADT/StringRefTest.cpp
@@ -619,6 +619,19 @@ TEST(StringRefTest, Hashing) {
hash_value(StringRef("hello world").slice(1, -1)));
}
+TEST(StringRefTest, getAutoSenseRadix) {
+ struct RadixPair {
+ const char *Str;
+ unsigned Expected;
+ } RadixNumbers[] = {{"123", 10}, {"1", 10}, {"0b1", 2}, {"01", 8}, {"0o1", 8},
+ {"0x1", 16}, {"0", 10}, {"00", 8}, {"", 10}};
+ for (size_t i = 0; i < std::size(RadixNumbers); ++i) {
+ StringRef number = RadixNumbers[i].Str;
+ unsigned radix = getAutoSenseRadix(number);
+ EXPECT_EQ(radix, RadixNumbers[i].Expected);
+ }
+}
+
struct UnsignedPair {
const char *Str;
uint64_t Expected;