[libc-commits] [libc] 19c3894 - [libc] Fix couple of corner cases in remquo.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Wed Dec 2 11:49:02 PST 2020


Author: Siva Chandra Reddy
Date: 2020-12-02T11:48:49-08:00
New Revision: 19c3894f9436ef68f33f80ee1fd589166267b5a1

URL: https://github.com/llvm/llvm-project/commit/19c3894f9436ef68f33f80ee1fd589166267b5a1
DIFF: https://github.com/llvm/llvm-project/commit/19c3894f9436ef68f33f80ee1fd589166267b5a1.diff

LOG: [libc] Fix couple of corner cases in remquo.

These two cases are fixed:
1. If numerator is not zero and denominator is infinity, then the
numerator is returned as the remainder.
2. If numerator and denominator are equal in magnitude, then quotient
with the right sign is returned.

The differet tests of remquo, remquof and remquol have been unified
into a single file to avoid duplication.

Reviewed By: lntue

Differential Revision: https://reviews.llvm.org/D92353

Added: 
    libc/test/src/math/RemQuoTest.h

Modified: 
    libc/test/src/math/CMakeLists.txt
    libc/test/src/math/remquo_test.cpp
    libc/test/src/math/remquof_test.cpp
    libc/test/src/math/remquol_test.cpp
    libc/utils/FPUtil/DivisionAndRemainderOperations.h

Removed: 
    


################################################################################
diff  --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 2220cef00791..cdffe737d8df 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -686,6 +686,8 @@ add_fp_unittest(
     libc_math_unittests
   SRCS
     remquof_test.cpp
+  HDRS
+    RemQuoTest.h
   DEPENDS
     libc.include.math
     libc.src.math.remquof
@@ -699,6 +701,8 @@ add_fp_unittest(
     libc_math_unittests
   SRCS
     remquo_test.cpp
+  HDRS
+    RemQuoTest.h
   DEPENDS
     libc.include.math
     libc.src.math.remquo
@@ -712,6 +716,8 @@ add_fp_unittest(
     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
index 000000000000..29fcdb83b6a2
--- /dev/null
+++ b/libc/test/src/math/RemQuoTest.h
@@ -0,0 +1,144 @@
+//===-- Utility class to test 
diff erent 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 <math.h>
+
+namespace mpfr = __llvm_libc::testing::mpfr;
+
+template <typename T>
+class RemQuoTestTemplate : public __llvm_libc::testing::Test {
+  using FPBits = __llvm_libc::fputil::FPBits<T>;
+  using UIntType = typename FPBits::UIntType;
+
+  const T zero = __llvm_libc::fputil::FPBits<T>::zero();
+  const T negZero = __llvm_libc::fputil::FPBits<T>::negZero();
+  const T inf = __llvm_libc::fputil::FPBits<T>::inf();
+  const T negInf = __llvm_libc::fputil::FPBits<T>::negInf();
+  const T nan = __llvm_libc::fputil::FPBits<T>::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, &quotient)), 0);
+    x = negInf;
+    EXPECT_NE(isnan(func(x, y, &quotient)), 0);
+
+    x = T(1.0);
+    y = zero;
+    EXPECT_NE(isnan(func(x, y, &quotient)), 0);
+    y = negZero;
+    EXPECT_NE(isnan(func(x, y, &quotient)), 0);
+
+    y = nan;
+    x = T(1.0);
+    EXPECT_NE(isnan(func(x, y, &quotient)), 0);
+
+    y = T(1.0);
+    x = nan;
+    EXPECT_NE(isnan(func(x, y, &quotient)), 0);
+
+    x = nan;
+    y = nan;
+    EXPECT_NE(isnan(func(x, y, &quotient)), 0);
+
+    x = zero;
+    y = T(1.0);
+    EXPECT_FP_EQ(func(x, y, &quotient), zero);
+
+    x = negZero;
+    y = T(1.0);
+    EXPECT_FP_EQ(func(x, y, &quotient), negZero);
+
+    x = T(1.125);
+    y = inf;
+    EXPECT_FP_EQ(func(x, y, &quotient), 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<T> result;
+      mpfr::BinaryInput<T> 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<T> result;
+      mpfr::BinaryInput<T> 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<T>;                                    \
+  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
index 197d97069001..8efec397f39d 100644
--- 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 <math.h>
-
-using FPBits = __llvm_libc::fputil::FPBits<double>;
-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<double> result;
-    mpfr::BinaryInput<double> 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<double> result;
-    mpfr::BinaryInput<double> 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
index 4ede7fb3feec..1af4ba4e0153 100644
--- 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 <math.h>
-
-using FPBits = __llvm_libc::fputil::FPBits<float>;
-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<float> result;
-    mpfr::BinaryInput<float> 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<float> result;
-    mpfr::BinaryInput<float> 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
index 1539aabead95..e4438e83fe18 100644
--- 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 <math.h>
-
-using FPBits = __llvm_libc::fputil::FPBits<long double>;
-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<long double> result;
-    mpfr::BinaryInput<long double> 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<long double> 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<long double> 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
index ceae538027f3..b4732c7c5230 100644
--- a/libc/utils/FPUtil/DivisionAndRemainderOperations.h
+++ b/libc/utils/FPUtil/DivisionAndRemainderOperations.h
@@ -33,11 +33,16 @@ static inline T remquo(T x, T y, int &q) {
   if (xbits.isInf() || ybits.isZero())
     return FPBits<T>::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 @@ static inline T remquo(T x, T y, int &q) {
       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<T> remainder(exp + normaly.exponent, mx, 0);


        


More information about the libc-commits mailing list