/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include using namespace folly; using namespace folly::detail; namespace { // Workaround for https://llvm.org/bugs/show_bug.cgi?id=16404, // issues with __int128 multiplication and UBSAN template T mul(T lhs, T rhs) { if (rhs < 0) { rhs = -rhs; lhs = -lhs; } T accum = 0; while (rhs != 0) { if ((rhs & 1) != 0) { accum += lhs; } lhs += lhs; rhs >>= 1; } return accum; } template T referenceDivFloor(T numer, T denom) { // rv = largest integral value <= numer / denom B n = numer; B d = denom; if (d < 0) { d = -d; n = -n; } B r = n / d; while (mul(r, d) > n) { --r; } while (mul(r + 1, d) <= n) { ++r; } T rv = static_cast(r); assert(static_cast(rv) == r); return rv; } template T referenceDivCeil(T numer, T denom) { // rv = smallest integral value >= numer / denom B n = numer; B d = denom; if (d < 0) { d = -d; n = -n; } B r = n / d; while (mul(r, d) < n) { ++r; } while (mul(r - 1, d) >= n) { --r; } T rv = static_cast(r); assert(static_cast(rv) == r); return rv; } template T referenceDivRoundAway(T numer, T denom) { if ((numer < 0) != (denom < 0)) { return referenceDivFloor(numer, denom); } else { return referenceDivCeil(numer, denom); } } template std::vector cornerValues() { std::vector rv; for (T i = 1; i < 24; ++i) { rv.push_back(i); rv.push_back(T(std::numeric_limits::max() / i)); rv.push_back(T(std::numeric_limits::max() - i)); rv.push_back(T(std::numeric_limits::max() / T(2) - i)); if (std::is_signed::value) { rv.push_back(-i); rv.push_back(T(std::numeric_limits::min() / i)); rv.push_back(T(std::numeric_limits::min() + i)); rv.push_back(T(std::numeric_limits::min() / T(2) + i)); } } return rv; } template void runDivTests() { using T = decltype(static_cast(1) / static_cast(1)); auto numers = cornerValues(); numers.push_back(0); auto denoms = cornerValues(); for (A n : numers) { for (B d : denoms) { if (std::is_signed::value && n == std::numeric_limits::min() && d == static_cast(-1)) { // n / d overflows in two's complement continue; } EXPECT_EQ(divCeil(n, d), (referenceDivCeil(n, d))) << n << "/" << d; EXPECT_EQ(divFloor(n, d), (referenceDivFloor(n, d))) << n << "/" << d; EXPECT_EQ(divTrunc(n, d), n / d) << n << "/" << d; EXPECT_EQ(divRoundAway(n, d), (referenceDivRoundAway(n, d))) << n << "/" << d; T nn = n; T dd = d; EXPECT_EQ(divCeilBranchless(nn, dd), divCeilBranchful(nn, dd)); EXPECT_EQ(divFloorBranchless(nn, dd), divFloorBranchful(nn, dd)); EXPECT_EQ(divRoundAwayBranchless(nn, dd), divRoundAwayBranchful(nn, dd)); } } } } // namespace TEST(Bits, divTestInt8) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); #if FOLLY_HAVE_INT128_T runDivTests(); runDivTests(); #endif } TEST(Bits, divTestInt16) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); #if FOLLY_HAVE_INT128_T runDivTests(); runDivTests(); #endif } TEST(Bits, divTestInt32) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); #if FOLLY_HAVE_INT128_T runDivTests(); runDivTests(); #endif } #if FOLLY_HAVE_INT128_T TEST(Bits, divTestInt64) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); } #endif TEST(Bits, divTestUint8) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); #if FOLLY_HAVE_INT128_T runDivTests(); runDivTests(); #endif } TEST(Bits, divTestUint16) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); #if FOLLY_HAVE_INT128_T runDivTests(); runDivTests(); #endif } TEST(Bits, divTestUint32) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); #if FOLLY_HAVE_INT128_T runDivTests(); runDivTests(); #endif } #if FOLLY_HAVE_INT128_T TEST(Bits, divTestUint64) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); } #endif FOLLY_CREATE_FREE_INVOKER(midpoint_invoke, midpoint); TEST(MidpointTest, MidpointTest) { EXPECT_EQ(midpoint(2, 4), 3); EXPECT_EQ(midpoint(3, 4), 3); EXPECT_EQ(midpoint(-2, 2), 0); EXPECT_EQ(midpoint(-4, -2), -3); EXPECT_EQ(midpoint(102, 104), 103); EXPECT_EQ(midpoint(126, 126), 126); EXPECT_EQ(midpoint(-104, -102), -103); // Perform some simple tests. Note that because these are small integers // they can be represented exactly, so we do not have floating-point error. EXPECT_EQ(midpoint(2.0, 4.0), 3.0); EXPECT_EQ(midpoint(-2.0, 2.0), 0.0); EXPECT_EQ(midpoint(-2.1, 2.1), 0.0); EXPECT_EQ(midpoint(-4.0, -2.0), -3.0); EXPECT_EQ(midpoint(102.0, 104.0), 103.0); EXPECT_EQ(midpoint(-104.0, -102.0), -103.0); // Double EXPECT_EQ(midpoint(2.0, 4.0), 3.0); EXPECT_EQ(midpoint(0.0, 0.4), 0.2); EXPECT_EQ(midpoint(0.0, -0.0), 0.0); EXPECT_EQ(midpoint(9e9, -9e9), 0.0); EXPECT_EQ( midpoint( std::numeric_limits::max(), std::numeric_limits::max()), std::numeric_limits::max()); EXPECT_TRUE(std::isnan(midpoint( -std::numeric_limits::infinity(), std::numeric_limits::infinity()))); // Float EXPECT_EQ(midpoint(2.0f, 4.0f), 3.0f); EXPECT_EQ(midpoint(0.0f, 0.4f), 0.2f); EXPECT_EQ(midpoint(0.0f, -0.0f), 0.0f); EXPECT_EQ(midpoint(9e9f, -9e9f), 0.0f); EXPECT_EQ( midpoint( std::numeric_limits::max(), std::numeric_limits::max()), std::numeric_limits::max()); EXPECT_TRUE(std::isnan(midpoint( -std::numeric_limits::infinity(), std::numeric_limits::infinity()))); // Long double EXPECT_EQ(midpoint(2.0l, 4.0l), 3.0l); EXPECT_EQ(midpoint(0.0l, 0.4l), 0.2l); EXPECT_EQ(midpoint(0.0l, -0.0l), 0.0l); EXPECT_EQ(midpoint(9e9l, -9e9l), 0.0l); EXPECT_EQ( midpoint( std::numeric_limits::max(), std::numeric_limits::max()), std::numeric_limits::max()); EXPECT_TRUE(std::isnan(midpoint( -std::numeric_limits::infinity(), std::numeric_limits::infinity()))); EXPECT_TRUE(noexcept(midpoint(1, 2))); EXPECT_FALSE((is_invocable_v)); EXPECT_FALSE((is_invocable_v)); EXPECT_FALSE((is_invocable_v)); constexpr auto MY_INT_MAX = std::numeric_limits::max(); constexpr auto MY_INT_MIN = std::numeric_limits::min(); constexpr auto MY_UINT_MAX = std::numeric_limits::max(); constexpr auto MY_SHRT_MAX = std::numeric_limits::max(); constexpr auto MY_SHRT_MIN = std::numeric_limits::min(); constexpr auto MY_SCHAR_MAX = std::numeric_limits::max(); constexpr auto MY_SCHAR_MIN = std::numeric_limits::min(); EXPECT_EQ(midpoint(0, 0), 0); EXPECT_EQ(midpoint(1, 1), 1); EXPECT_EQ(midpoint(0, 1), 0); EXPECT_EQ(midpoint(1, 0), 1); EXPECT_EQ(midpoint(0, 2), 1); EXPECT_EQ(midpoint(3, 2), 3); EXPECT_EQ(midpoint(-5, 4), -1); EXPECT_EQ(midpoint(5, -4), 1); EXPECT_EQ(midpoint(-5, -4), -5); EXPECT_EQ(midpoint(-4, -5), -4); EXPECT_EQ(midpoint(MY_INT_MIN, MY_INT_MAX), -1); EXPECT_EQ(midpoint(MY_INT_MAX, MY_INT_MIN), 0); EXPECT_EQ(midpoint(MY_INT_MAX, MY_INT_MAX), MY_INT_MAX); EXPECT_EQ(midpoint(MY_INT_MAX, MY_INT_MAX - 1), MY_INT_MAX); EXPECT_EQ(midpoint(MY_INT_MAX - 1, MY_INT_MAX - 1), MY_INT_MAX - 1); EXPECT_EQ(midpoint(MY_INT_MAX - 1, MY_INT_MAX), MY_INT_MAX - 1); EXPECT_EQ(midpoint(MY_INT_MAX, MY_INT_MAX - 2), MY_INT_MAX - 1); EXPECT_EQ(midpoint(0u, 0u), 0); EXPECT_EQ(midpoint(0u, 1u), 0); EXPECT_EQ(midpoint(1u, 0u), 1); EXPECT_EQ(midpoint(0u, 2u), 1); EXPECT_EQ(midpoint(3u, 2u), 3); EXPECT_EQ(midpoint(0u, MY_UINT_MAX), MY_UINT_MAX / 2); EXPECT_EQ(midpoint(MY_UINT_MAX, 0u), (MY_UINT_MAX / 2 + 1)); EXPECT_EQ(midpoint(MY_UINT_MAX, MY_UINT_MAX), MY_UINT_MAX); EXPECT_EQ(midpoint(MY_UINT_MAX, MY_UINT_MAX - 1), MY_UINT_MAX); EXPECT_EQ(midpoint(MY_UINT_MAX - 1, MY_UINT_MAX - 1), MY_UINT_MAX - 1); EXPECT_EQ(midpoint(MY_UINT_MAX - 1, MY_UINT_MAX), MY_UINT_MAX - 1); EXPECT_EQ(midpoint(MY_UINT_MAX, MY_UINT_MAX - 2), MY_UINT_MAX - 1); EXPECT_EQ(midpoint(0, 0), 0); EXPECT_EQ(midpoint(0, 1), 0); EXPECT_EQ(midpoint(1, 0), 1); EXPECT_EQ(midpoint(0, 2), 1); EXPECT_EQ(midpoint(3, 2), 3); EXPECT_EQ(midpoint(-5, 4), -1); EXPECT_EQ(midpoint(5, -4), 1); EXPECT_EQ(midpoint(-5, -4), -5); EXPECT_EQ(midpoint(-4, -5), -4); EXPECT_EQ(midpoint(MY_SHRT_MIN, MY_SHRT_MAX), -1); EXPECT_EQ(midpoint(MY_SHRT_MAX, MY_SHRT_MIN), 0); EXPECT_EQ(midpoint(MY_SHRT_MAX, MY_SHRT_MAX), MY_SHRT_MAX); EXPECT_EQ(midpoint(MY_SHRT_MAX, MY_SHRT_MAX - 1), MY_SHRT_MAX); EXPECT_EQ(midpoint(MY_SHRT_MAX - 1, MY_SHRT_MAX - 1), MY_SHRT_MAX - 1); EXPECT_EQ(midpoint(MY_SHRT_MAX - 1, MY_SHRT_MAX), MY_SHRT_MAX - 1); EXPECT_EQ(midpoint(MY_SHRT_MAX, MY_SHRT_MAX - 2), MY_SHRT_MAX - 1); EXPECT_EQ(midpoint(0, 0), 0); EXPECT_EQ(midpoint(1, 1), 1); EXPECT_EQ(midpoint(0, 1), 0); EXPECT_EQ(midpoint(1, 0), 1); EXPECT_EQ(midpoint(0, 2), 1); EXPECT_EQ(midpoint(3, 2), 3); EXPECT_EQ(midpoint(-5, 4), -1); EXPECT_EQ(midpoint(5, -4), 1); EXPECT_EQ(midpoint(-5, -4), -5); EXPECT_EQ(midpoint(-4, -5), -4); EXPECT_EQ(midpoint(MY_SCHAR_MIN, MY_SCHAR_MAX), -1); EXPECT_EQ(midpoint(MY_SCHAR_MAX, MY_SCHAR_MIN), 0); EXPECT_EQ(midpoint(MY_SCHAR_MAX, MY_SCHAR_MAX), MY_SCHAR_MAX); EXPECT_EQ( midpoint(MY_SCHAR_MAX, MY_SCHAR_MAX - 1), MY_SCHAR_MAX); constexpr auto MY_SIZE_T_MAX = std::numeric_limits::max(); EXPECT_EQ(midpoint(0, 0), 0); EXPECT_EQ(midpoint(1, 1), 1); EXPECT_EQ(midpoint(0, 1), 0); EXPECT_EQ(midpoint(1, 0), 1); EXPECT_EQ(midpoint(0, 2), 1); EXPECT_EQ(midpoint(3, 2), 3); EXPECT_EQ(midpoint((size_t)0, MY_SIZE_T_MAX), MY_SIZE_T_MAX / 2); EXPECT_EQ( midpoint(MY_SIZE_T_MAX, (size_t)0), (MY_SIZE_T_MAX / 2 + 1)); EXPECT_EQ(midpoint(MY_SIZE_T_MAX, MY_SIZE_T_MAX), MY_SIZE_T_MAX); EXPECT_EQ(midpoint(MY_SIZE_T_MAX, MY_SIZE_T_MAX - 1), MY_SIZE_T_MAX); EXPECT_EQ( midpoint(MY_SIZE_T_MAX - 1, MY_SIZE_T_MAX - 1), MY_SIZE_T_MAX - 1); EXPECT_EQ( midpoint(MY_SIZE_T_MAX - 1, MY_SIZE_T_MAX), MY_SIZE_T_MAX - 1); EXPECT_EQ( midpoint(MY_SIZE_T_MAX, MY_SIZE_T_MAX - 2), MY_SIZE_T_MAX - 1); #if FOLLY_HAVE_INT128_T const auto I128_MIN = std::numeric_limits<__int128_t>::min(); const auto I128_MAX = std::numeric_limits<__int128_t>::max(); EXPECT_EQ(midpoint<__int128_t>(0, 0), 0); EXPECT_EQ(midpoint<__int128_t>(1, 1), 1); EXPECT_EQ(midpoint<__int128_t>(0, 1), 0); EXPECT_EQ(midpoint<__int128_t>(1, 0), 1); EXPECT_EQ(midpoint<__int128_t>(0, 2), 1); EXPECT_EQ(midpoint<__int128_t>(3, 2), 3); EXPECT_EQ(midpoint<__int128_t>(-5, 4), -1); EXPECT_EQ(midpoint<__int128_t>(5, -4), 1); EXPECT_EQ(midpoint<__int128_t>(-5, -4), -5); EXPECT_EQ(midpoint<__int128_t>(-4, -5), -4); EXPECT_EQ(midpoint<__int128_t>(I128_MIN, I128_MAX), -1); EXPECT_EQ(midpoint<__int128_t>(I128_MAX, I128_MIN), 0); EXPECT_EQ(midpoint<__int128_t>(I128_MAX, I128_MAX), I128_MAX); EXPECT_EQ(midpoint<__int128_t>(I128_MAX, I128_MAX - 1), I128_MAX); #endif // Test every possibility for signed char. for (int a = MY_SCHAR_MIN; a <= MY_SCHAR_MAX; ++a) for (int b = MY_SCHAR_MIN; b <= MY_SCHAR_MAX; ++b) EXPECT_EQ(midpoint(a, b), midpoint(a, b)); EXPECT_FALSE((is_invocable_v)); EXPECT_FALSE((is_invocable_v)); EXPECT_FALSE((is_invocable_v)); constexpr std::array ca = {0, 1, 2}; EXPECT_EQ(midpoint(ca.data(), ca.data() + 3), ca.data() + 1); constexpr std::array a = {0, 1, 2, 3}; EXPECT_EQ(midpoint(a.data(), a.data()), a.data()); EXPECT_EQ(midpoint(a.data(), a.data() + 1), a.data()); EXPECT_EQ(midpoint(a.data(), a.data() + 2), a.data() + 1); EXPECT_EQ(midpoint(a.data(), a.data() + 3), a.data() + 1); EXPECT_EQ(midpoint(a.data(), a.data() + 4), a.data() + 2); EXPECT_EQ(midpoint(a.data() + 1, a.data()), a.data() + 1); EXPECT_EQ(midpoint(a.data() + 2, a.data()), a.data() + 1); EXPECT_EQ(midpoint(a.data() + 3, a.data()), a.data() + 2); EXPECT_EQ(midpoint(a.data() + 4, a.data()), a.data() + 2); }