diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -686,6 +686,8 @@ libc_math_unittests SRCS remquof_test.cpp + HDRS + RemQuoTest.h DEPENDS libc.include.math libc.src.math.remquof @@ -699,6 +701,8 @@ libc_math_unittests SRCS remquo_test.cpp + HDRS + RemQuoTest.h DEPENDS libc.include.math libc.src.math.remquo @@ -712,6 +716,8 @@ libc_math_unittests SRCS remquol_test.cpp + HDRS + RemQuoTest.h DEPENDS libc.include.math libc.src.math.remquol diff --git a/libc/test/src/math/RemQuoTest.h b/libc/test/src/math/RemQuoTest.h new file mode 100644 --- /dev/null +++ b/libc/test/src/math/RemQuoTest.h @@ -0,0 +1,144 @@ +//===-- Utility class to test different flavors of remquo -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_TEST_SRC_MATH_REMQUOTEST_H +#define LLVM_LIBC_TEST_SRC_MATH_REMQUOTEST_H + +#include "utils/FPUtil/BasicOperations.h" +#include "utils/FPUtil/FPBits.h" +#include "utils/FPUtil/TestHelpers.h" +#include "utils/MPFRWrapper/MPFRUtils.h" +#include "utils/UnitTest/Test.h" +#include + +namespace mpfr = __llvm_libc::testing::mpfr; + +template +class RemQuoTestTemplate : public __llvm_libc::testing::Test { + using FPBits = __llvm_libc::fputil::FPBits; + using UIntType = typename FPBits::UIntType; + + const T zero = __llvm_libc::fputil::FPBits::zero(); + const T negZero = __llvm_libc::fputil::FPBits::negZero(); + const T inf = __llvm_libc::fputil::FPBits::inf(); + const T negInf = __llvm_libc::fputil::FPBits::negInf(); + const T nan = __llvm_libc::fputil::FPBits::buildNaN(1); + +public: + typedef T (*RemQuoFunc)(T, T, int *); + + void testSpecialNumbers(RemQuoFunc func) { + int quotient; + T x, y; + + y = T(1.0); + x = inf; + EXPECT_NE(isnan(func(x, y, "ient)), 0); + x = negInf; + EXPECT_NE(isnan(func(x, y, "ient)), 0); + + x = T(1.0); + y = zero; + EXPECT_NE(isnan(func(x, y, "ient)), 0); + y = negZero; + EXPECT_NE(isnan(func(x, y, "ient)), 0); + + y = nan; + x = T(1.0); + EXPECT_NE(isnan(func(x, y, "ient)), 0); + + y = T(1.0); + x = nan; + EXPECT_NE(isnan(func(x, y, "ient)), 0); + + x = nan; + y = nan; + EXPECT_NE(isnan(func(x, y, "ient)), 0); + + x = zero; + y = T(1.0); + EXPECT_FP_EQ(func(x, y, "ient), zero); + + x = negZero; + y = T(1.0); + EXPECT_FP_EQ(func(x, y, "ient), negZero); + + x = T(1.125); + y = inf; + EXPECT_FP_EQ(func(x, y, "ient), x); + EXPECT_EQ(quotient, 0); + } + + void testEqualNumeratorAndDenominator(RemQuoFunc func) { + T x = T(1.125), y = T(1.125); + int q; + + // When the remainder is zero, the standard requires it to + // have the same sign as x. + + EXPECT_FP_EQ(func(x, y, &q), zero); + EXPECT_EQ(q, 1); + + EXPECT_FP_EQ(func(x, -y, &q), zero); + EXPECT_EQ(q, -1); + + EXPECT_FP_EQ(func(-x, y, &q), negZero); + EXPECT_EQ(q, -1); + + EXPECT_FP_EQ(func(-x, -y, &q), negZero); + EXPECT_EQ(q, 1); + } + + void testSubnormalRange(RemQuoFunc func) { + constexpr UIntType count = 1000001; + constexpr UIntType step = + (FPBits::maxSubnormal - FPBits::minSubnormal) / count; + for (UIntType v = FPBits::minSubnormal, w = FPBits::maxSubnormal; + v <= FPBits::maxSubnormal && w >= FPBits::minSubnormal; + v += step, w -= step) { + T x = FPBits(v), y = FPBits(w); + mpfr::BinaryOutput result; + mpfr::BinaryInput input{x, y}; + result.f = func(x, y, &result.i); + ASSERT_MPFR_MATCH(mpfr::Operation::RemQuo, input, result, 0.0); + } + } + + void testNormalRange(RemQuoFunc func) { + constexpr UIntType count = 1000001; + constexpr UIntType step = (FPBits::maxNormal - FPBits::minNormal) / count; + for (UIntType v = FPBits::minNormal, w = FPBits::maxNormal; + v <= FPBits::maxNormal && w >= FPBits::minNormal; + v += step, w -= step) { + T x = FPBits(v), y = FPBits(w); + mpfr::BinaryOutput result; + mpfr::BinaryInput input{x, y}; + result.f = func(x, y, &result.i); + + // In normal range on x86 platforms, the long double implicit 1 bit can be + // zero making the numbers NaN. Hence we test for them separately. + if (isnan(x) || isnan(y)) { + ASSERT_NE(isnan(result.f), 0); + continue; + } + + ASSERT_MPFR_MATCH(mpfr::Operation::RemQuo, input, result, 0.0); + } + } +}; + +#define LIST_REMQUO_TESTS(T, func) \ + using RemQuoTest = RemQuoTestTemplate; \ + TEST_F(RemQuoTest, SpecialNumbers) { testSpecialNumbers(&func); } \ + TEST_F(RemQuoTest, EqualNumeratorAndDenominator) { \ + testEqualNumeratorAndDenominator(&func); \ + } \ + TEST_F(RemQuoTest, SubnormalRange) { testSubnormalRange(&func); } \ + TEST_F(RemQuoTest, NormalRange) { testNormalRange(&func); } + +#endif // LLVM_LIBC_TEST_SRC_MATH_REMQUOTEST_H diff --git a/libc/test/src/math/remquo_test.cpp b/libc/test/src/math/remquo_test.cpp --- a/libc/test/src/math/remquo_test.cpp +++ b/libc/test/src/math/remquo_test.cpp @@ -6,82 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "src/math/remquo.h" -#include "utils/FPUtil/BasicOperations.h" -#include "utils/FPUtil/FPBits.h" -#include "utils/FPUtil/TestHelpers.h" -#include "utils/MPFRWrapper/MPFRUtils.h" -#include "utils/UnitTest/Test.h" -#include - -using FPBits = __llvm_libc::fputil::FPBits; -using UIntType = FPBits::UIntType; - -namespace mpfr = __llvm_libc::testing::mpfr; - -DECLARE_SPECIAL_CONSTANTS(double) - -TEST(RemquoTest, SpecialNumbers) { - int exponent; - double x, y; - - y = 1.0; - x = inf; - EXPECT_NE(isnan(__llvm_libc::remquo(x, y, &exponent)), 0); - x = negInf; - EXPECT_NE(isnan(__llvm_libc::remquo(x, y, &exponent)), 0); - - x = 1.0; - y = zero; - EXPECT_NE(isnan(__llvm_libc::remquo(x, y, &exponent)), 0); - y = negZero; - EXPECT_NE(isnan(__llvm_libc::remquo(x, y, &exponent)), 0); +#include "RemQuoTest.h" - y = nan; - x = 1.0; - EXPECT_NE(isnan(__llvm_libc::remquo(x, y, &exponent)), 0); - - y = 1.0; - x = nan; - EXPECT_NE(isnan(__llvm_libc::remquo(x, y, &exponent)), 0); - - x = nan; - y = nan; - EXPECT_NE(isnan(__llvm_libc::remquo(x, y, &exponent)), 0); - - x = zero; - y = 1.0; - EXPECT_FP_EQ(__llvm_libc::remquo(x, y, &exponent), zero); - - x = negZero; - y = 1.0; - EXPECT_FP_EQ(__llvm_libc::remquo(x, y, &exponent), negZero); -} - -TEST(RemquoTest, SubnormalRange) { - constexpr UIntType count = 1000001; - constexpr UIntType step = - (FPBits::maxSubnormal - FPBits::minSubnormal) / count; - for (UIntType v = FPBits::minSubnormal, w = FPBits::maxSubnormal; - v <= FPBits::maxSubnormal && w >= FPBits::minSubnormal; - v += step, w -= step) { - double x = FPBits(v), y = FPBits(w); - mpfr::BinaryOutput result; - mpfr::BinaryInput input{x, y}; - result.f = __llvm_libc::remquo(x, y, &result.i); - ASSERT_MPFR_MATCH(mpfr::Operation::RemQuo, input, result, 0.0); - } -} +#include "src/math/remquo.h" -TEST(RemquoTest, NormalRange) { - constexpr UIntType count = 1000001; - constexpr UIntType step = (FPBits::maxNormal - FPBits::minNormal) / count; - for (UIntType v = FPBits::minNormal, w = FPBits::maxNormal; - v <= FPBits::maxNormal && w >= FPBits::minNormal; v += step, w -= step) { - double x = FPBits(v), y = FPBits(w); - mpfr::BinaryOutput result; - mpfr::BinaryInput input{x, y}; - result.f = __llvm_libc::remquo(x, y, &result.i); - ASSERT_MPFR_MATCH(mpfr::Operation::RemQuo, input, result, 0.0); - } -} +LIST_REMQUO_TESTS(double, __llvm_libc::remquo) diff --git a/libc/test/src/math/remquof_test.cpp b/libc/test/src/math/remquof_test.cpp --- a/libc/test/src/math/remquof_test.cpp +++ b/libc/test/src/math/remquof_test.cpp @@ -6,82 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "src/math/remquof.h" -#include "utils/FPUtil/BasicOperations.h" -#include "utils/FPUtil/FPBits.h" -#include "utils/FPUtil/TestHelpers.h" -#include "utils/MPFRWrapper/MPFRUtils.h" -#include "utils/UnitTest/Test.h" -#include - -using FPBits = __llvm_libc::fputil::FPBits; -using UIntType = FPBits::UIntType; - -namespace mpfr = __llvm_libc::testing::mpfr; - -DECLARE_SPECIAL_CONSTANTS(float) - -TEST(RemquofTest, SpecialNumbers) { - int exponent; - float x, y; - - y = 1.0f; - x = inf; - EXPECT_NE(isnan(__llvm_libc::remquof(x, y, &exponent)), 0); - x = negInf; - EXPECT_NE(isnan(__llvm_libc::remquof(x, y, &exponent)), 0); - - x = 1.0f; - y = zero; - EXPECT_NE(isnan(__llvm_libc::remquof(x, y, &exponent)), 0); - y = negZero; - EXPECT_NE(isnan(__llvm_libc::remquof(x, y, &exponent)), 0); +#include "RemQuoTest.h" - y = nan; - x = 1.0f; - EXPECT_NE(isnan(__llvm_libc::remquof(x, y, &exponent)), 0); - - y = 1.0f; - x = nan; - EXPECT_NE(isnan(__llvm_libc::remquof(x, y, &exponent)), 0); - - x = nan; - y = nan; - EXPECT_NE(isnan(__llvm_libc::remquof(x, y, &exponent)), 0); - - x = zero; - y = 1.0f; - EXPECT_FP_EQ(__llvm_libc::remquof(x, y, &exponent), zero); - - x = negZero; - y = 1.0f; - EXPECT_FP_EQ(__llvm_libc::remquof(x, y, &exponent), negZero); -} - -TEST(RemquofTest, SubnormalRange) { - constexpr UIntType count = 1000001; - constexpr UIntType step = - (FPBits::maxSubnormal - FPBits::minSubnormal) / count; - for (UIntType v = FPBits::minSubnormal, w = FPBits::maxSubnormal; - v <= FPBits::maxSubnormal && w >= FPBits::minSubnormal; - v += step, w -= step) { - float x = FPBits(v), y = FPBits(w); - mpfr::BinaryOutput result; - mpfr::BinaryInput input{x, y}; - result.f = __llvm_libc::remquof(x, y, &result.i); - ASSERT_MPFR_MATCH(mpfr::Operation::RemQuo, input, result, 0.0); - } -} +#include "src/math/remquof.h" -TEST(RemquofTest, NormalRange) { - constexpr UIntType count = 1000001; - constexpr UIntType step = (FPBits::maxNormal - FPBits::minNormal) / count; - for (UIntType v = FPBits::minNormal, w = FPBits::maxNormal; - v <= FPBits::maxNormal && w >= FPBits::minNormal; v += step, w -= step) { - float x = FPBits(v), y = FPBits(w); - mpfr::BinaryOutput result; - mpfr::BinaryInput input{x, y}; - result.f = __llvm_libc::remquof(x, y, &result.i); - ASSERT_MPFR_MATCH(mpfr::Operation::RemQuo, input, result, 0.0); - } -} +LIST_REMQUO_TESTS(float, __llvm_libc::remquof) diff --git a/libc/test/src/math/remquol_test.cpp b/libc/test/src/math/remquol_test.cpp --- a/libc/test/src/math/remquol_test.cpp +++ b/libc/test/src/math/remquol_test.cpp @@ -6,88 +6,8 @@ // //===----------------------------------------------------------------------===// -#include "src/math/remquol.h" -#include "utils/FPUtil/BasicOperations.h" -#include "utils/FPUtil/FPBits.h" -#include "utils/FPUtil/TestHelpers.h" -#include "utils/MPFRWrapper/MPFRUtils.h" -#include "utils/UnitTest/Test.h" -#include - -using FPBits = __llvm_libc::fputil::FPBits; -using UIntType = FPBits::UIntType; - -namespace mpfr = __llvm_libc::testing::mpfr; - -DECLARE_SPECIAL_CONSTANTS(long double) - -TEST(RemquolTest, SpecialNumbers) { - int exponent; - long double x, y; - - y = 1.0l; - x = inf; - EXPECT_NE(isnan(__llvm_libc::remquol(x, y, &exponent)), 0); - x = negInf; - EXPECT_NE(isnan(__llvm_libc::remquol(x, y, &exponent)), 0); - - x = 1.0l; - y = zero; - EXPECT_NE(isnan(__llvm_libc::remquol(x, y, &exponent)), 0); - y = negZero; - EXPECT_NE(isnan(__llvm_libc::remquol(x, y, &exponent)), 0); +#include "RemQuoTest.h" - y = nan; - x = 1.0l; - EXPECT_NE(isnan(__llvm_libc::remquol(x, y, &exponent)), 0); - - y = 1.0l; - x = nan; - EXPECT_NE(isnan(__llvm_libc::remquol(x, y, &exponent)), 0); - - x = nan; - y = nan; - EXPECT_NE(isnan(__llvm_libc::remquol(x, y, &exponent)), 0); - - x = zero; - y = 1.0l; - EXPECT_FP_EQ(__llvm_libc::remquol(x, y, &exponent), zero); - - x = negZero; - y = 1.0l; - EXPECT_FP_EQ(__llvm_libc::remquol(x, y, &exponent), negZero); -} - -TEST(RemquolTest, SubnormalRange) { - constexpr UIntType count = 1000001; - constexpr UIntType step = - (FPBits::maxSubnormal - FPBits::minSubnormal) / count; - for (UIntType v = FPBits::minSubnormal, w = FPBits::maxSubnormal; - v <= FPBits::maxSubnormal && w >= FPBits::minSubnormal; - v += step, w -= step) { - long double x = FPBits(v), y = FPBits(w); - mpfr::BinaryOutput result; - mpfr::BinaryInput input{x, y}; - result.f = __llvm_libc::remquol(x, y, &result.i); - ASSERT_MPFR_MATCH(mpfr::Operation::RemQuo, input, result, 0.0); - } -} +#include "src/math/remquol.h" -TEST(RemquolTest, NormalRange) { - constexpr UIntType count = 1000001; - constexpr UIntType step = (FPBits::maxNormal - FPBits::minNormal) / count; - for (UIntType v = FPBits::minNormal, w = FPBits::maxNormal; - v <= FPBits::maxNormal && w >= FPBits::minNormal; v += step, w -= step) { - long double x = FPBits(v), y = FPBits(w); - mpfr::BinaryOutput result; - result.f = __llvm_libc::remquol(x, y, &result.i); - // In normal range on x86 platforms, the implicit 1 bit can be zero making - // the numbers NaN. Hence we test for them separately. - if (isnan(x) || isnan(y)) { - ASSERT_NE(isnan(result.f), 0); - } else { - mpfr::BinaryInput input{x, y}; - ASSERT_MPFR_MATCH(mpfr::Operation::RemQuo, input, result, 0.0); - } - } -} +LIST_REMQUO_TESTS(long double, __llvm_libc::remquol) diff --git a/libc/utils/FPUtil/DivisionAndRemainderOperations.h b/libc/utils/FPUtil/DivisionAndRemainderOperations.h --- a/libc/utils/FPUtil/DivisionAndRemainderOperations.h +++ b/libc/utils/FPUtil/DivisionAndRemainderOperations.h @@ -33,11 +33,16 @@ if (xbits.isInf() || ybits.isZero()) return FPBits::buildNaN(1); - if (xbits.isZero() || ybits.isInf()) { + if (xbits.isZero()) { q = 0; return __llvm_libc::fputil::copysign(T(0.0), x); } + if (ybits.isInf()) { + q = 0; + return x; + } + bool resultSign = (xbits.sign == ybits.sign ? false : true); // Once we know the sign of the result, we can just operate on the absolute @@ -65,8 +70,10 @@ q |= (1 << exp); mx = n - my; - if (mx == 0) + if (mx == 0) { + q = resultSign ? -q : q; return __llvm_libc::fputil::copysign(T(0.0), x); + } } NormalFloat remainder(exp + normaly.exponent, mx, 0);