[libc] Add implementations of round and roundf.
authorSiva Chandra Reddy <sivachandra@google.com>
Fri, 29 May 2020 06:03:32 +0000 (23:03 -0700)
committerSiva Chandra Reddy <sivachandra@google.com>
Wed, 10 Jun 2020 06:01:20 +0000 (23:01 -0700)
Reviewers: asteinhauser

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

12 files changed:
libc/config/linux/api.td
libc/config/linux/x86_64/entrypoints.txt
libc/lib/CMakeLists.txt
libc/spec/stdc.td
libc/src/math/CMakeLists.txt
libc/src/math/round.cpp
libc/src/math/roundf.cpp [moved from libc/src/math/round_redirector.cpp with 60% similarity]
libc/src/math/roundf.h [new file with mode: 0644]
libc/test/src/math/CMakeLists.txt
libc/test/src/math/round_test.cpp [new file with mode: 0644]
libc/test/src/math/roundf_test.cpp [new file with mode: 0644]
libc/utils/FPUtil/FloatOperations.h

index 5ac6b9d..1d20c5f 100644 (file)
@@ -160,6 +160,7 @@ def MathAPI : PublicAPI<"math.h"> {
    "expf",
    "exp2f",
    "round",
+   "roundf",
    "sincosf",
    "sinf",
    "trunc",
index 47329d2..a6bc492 100644 (file)
@@ -55,6 +55,7 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.floor
     libc.src.math.floorf
     libc.src.math.round
+    libc.src.math.roundf
     libc.src.math.sincosf
     libc.src.math.sinf
     libc.src.math.trunc
index 1634053..b2b872c 100644 (file)
@@ -9,9 +9,3 @@ add_entrypoint_library(
   DEPENDS
   ${TARGET_LIBM_ENTRYPOINTS}
 )
-
-add_redirector_library(
-  llvmlibc_redirectors
-  DEPENDS
-    round_redirector
-)
index 077dc21..a9bdfbd 100644 (file)
@@ -203,6 +203,7 @@ def StdC : StandardSpec<"stdc"> {
           FunctionSpec<"exp2f", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
 
           FunctionSpec<"round", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
+          FunctionSpec<"roundf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
 
           FunctionSpec<"trunc", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
           FunctionSpec<"truncf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
index a3b1b4f..701ee6e 100644 (file)
@@ -21,21 +21,6 @@ add_object_library(
 )
 
 add_entrypoint_object(
-  round
-  REDIRECTED
-  SRCS
-    round.cpp
-  HDRS
-    round.h
-)
-
-add_redirector_object(
-  round_redirector
-  SRC
-    round_redirector.cpp
-)
-
-add_entrypoint_object(
   cosf
   SRCS
     cosf.cpp
@@ -151,6 +136,26 @@ add_entrypoint_object(
     libc.utils.FPUtil.fputil
 )
 
+add_entrypoint_object(
+  round
+  SRCS
+    round.cpp
+  HDRS
+    round.h
+  DEPENDS
+    libc.utils.FPUtil.fputil
+)
+
+add_entrypoint_object(
+  roundf
+  SRCS
+    roundf.cpp
+  HDRS
+    roundf.h
+  DEPENDS
+    libc.utils.FPUtil.fputil
+)
+
 add_object_library(
   exp_utils
   HDRS
index d8d5d29..0a6e548 100644 (file)
@@ -1,4 +1,4 @@
-//===-- Implementation of round -------------------------------------------===//
+//===-- Implementation of round function ----------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,16 +6,11 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "src/math/round.h"
-
 #include "src/__support/common.h"
+#include "utils/FPUtil/FloatOperations.h"
 
 namespace __llvm_libc {
 
-double __round_redirector(double x);
-
-double LLVM_LIBC_ENTRYPOINT(round)(double x) {
-  return __round_redirector(x);
-}
+double LLVM_LIBC_ENTRYPOINT(round)(double x) { return fputil::round(x); }
 
 } // namespace __llvm_libc
similarity index 60%
rename from libc/src/math/round_redirector.cpp
rename to libc/src/math/roundf.cpp
index 67cd8f7..bdfcd0a 100644 (file)
@@ -1,4 +1,4 @@
-//===-- Implementation of round redirector --------------------------------===//
+//===-- Implementation of roundf function ---------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,14 +6,11 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Include okay for this redirector.
-// NOLINTNEXTLINE(llvmlibc-restrict-system-libc-headers)
-#include <math.h>
+#include "src/__support/common.h"
+#include "utils/FPUtil/FloatOperations.h"
 
 namespace __llvm_libc {
 
-double __round_redirector(double x) {
-  return ::round(x);
-}
+float LLVM_LIBC_ENTRYPOINT(roundf)(float x) { return fputil::round(x); }
 
 } // namespace __llvm_libc
diff --git a/libc/src/math/roundf.h b/libc/src/math/roundf.h
new file mode 100644 (file)
index 0000000..6bab35a
--- /dev/null
@@ -0,0 +1,18 @@
+//===-- Implementation header for roundf ------------------------*- 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_SRC_MATH_ROUNDF_H
+#define LLVM_LIBC_SRC_MATH_ROUNDF_H
+
+namespace __llvm_libc {
+
+float roundf(float x);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_MATH_ROUNDF_H
index 3568b20..c381ea8 100644 (file)
@@ -176,6 +176,32 @@ add_math_unittest(
 )
 
 add_math_unittest(
+  round_test
+  NEED_MPFR
+  SUITE
+    libc_math_unittests
+  SRCS
+    round_test.cpp
+  DEPENDS
+    libc.include.math
+    libc.src.math.round
+    libc.utils.FPUtil.fputil
+)
+
+add_math_unittest(
+  roundf_test
+  NEED_MPFR
+  SUITE
+    libc_math_unittests
+  SRCS
+    roundf_test.cpp
+  DEPENDS
+    libc.include.math
+    libc.src.math.roundf
+    libc.utils.FPUtil.fputil
+)
+
+add_math_unittest(
   expf_test
   NEED_MPFR
   SUITE
diff --git a/libc/test/src/math/round_test.cpp b/libc/test/src/math/round_test.cpp
new file mode 100644 (file)
index 0000000..9816b1f
--- /dev/null
@@ -0,0 +1,84 @@
+//===-- Unittests for round -----------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "include/math.h"
+#include "src/math/round.h"
+#include "utils/FPUtil/BitPatterns.h"
+#include "utils/FPUtil/FloatOperations.h"
+#include "utils/FPUtil/FloatProperties.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include "utils/UnitTest/Test.h"
+
+using __llvm_libc::fputil::valueAsBits;
+using __llvm_libc::fputil::valueFromBits;
+
+using BitPatterns = __llvm_libc::fputil::BitPatterns<double>;
+using Properties = __llvm_libc::fputil::FloatProperties<double>;
+
+namespace mpfr = __llvm_libc::testing::mpfr;
+
+// Zero tolerance; As in, exact match with MPFR result.
+static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::doublePrecision, 0,
+                                           0};
+
+TEST(RoundTest, SpecialNumbers) {
+  EXPECT_EQ(
+      BitPatterns::aQuietNaN,
+      valueAsBits(__llvm_libc::round(valueFromBits(BitPatterns::aQuietNaN))));
+  EXPECT_EQ(BitPatterns::aNegativeQuietNaN,
+            valueAsBits(__llvm_libc::round(
+                valueFromBits(BitPatterns::aNegativeQuietNaN))));
+
+  EXPECT_EQ(BitPatterns::aSignallingNaN,
+            valueAsBits(__llvm_libc::round(
+                valueFromBits(BitPatterns::aSignallingNaN))));
+  EXPECT_EQ(BitPatterns::aNegativeSignallingNaN,
+            valueAsBits(__llvm_libc::round(
+                valueFromBits(BitPatterns::aNegativeSignallingNaN))));
+
+  EXPECT_EQ(BitPatterns::inf,
+            valueAsBits(__llvm_libc::round(valueFromBits(BitPatterns::inf))));
+  EXPECT_EQ(BitPatterns::negInf, valueAsBits(__llvm_libc::round(
+                                     valueFromBits(BitPatterns::negInf))));
+
+  EXPECT_EQ(BitPatterns::zero,
+            valueAsBits(__llvm_libc::round(valueFromBits(BitPatterns::zero))));
+  EXPECT_EQ(BitPatterns::negZero, valueAsBits(__llvm_libc::round(
+                                      valueFromBits(BitPatterns::negZero))));
+}
+
+TEST(RoundTest, RoundedNumbers) {
+  EXPECT_EQ(valueAsBits(1.0), valueAsBits(__llvm_libc::round(1.0)));
+  EXPECT_EQ(valueAsBits(-1.0), valueAsBits(__llvm_libc::round(-1.0)));
+  EXPECT_EQ(valueAsBits(10.0), valueAsBits(__llvm_libc::round(10.0)));
+  EXPECT_EQ(valueAsBits(-10.0), valueAsBits(__llvm_libc::round(-10.0)));
+  EXPECT_EQ(valueAsBits(12345.0), valueAsBits(__llvm_libc::round(12345.0)));
+  EXPECT_EQ(valueAsBits(-12345.0), valueAsBits(__llvm_libc::round(-12345.0)));
+}
+
+TEST(RoundTest, CloseToZeroNumbers) {
+  EXPECT_EQ(valueAsBits(1.0), valueAsBits(__llvm_libc::round(0.5)));
+  EXPECT_EQ(valueAsBits(-1.0), valueAsBits(__llvm_libc::round(-0.5)));
+  EXPECT_EQ(valueAsBits(0.0), valueAsBits(__llvm_libc::round(0.115)));
+  EXPECT_EQ(valueAsBits(-0.0), valueAsBits(__llvm_libc::round(-0.115)));
+  EXPECT_EQ(valueAsBits(1.0), valueAsBits(__llvm_libc::round(0.715)));
+  EXPECT_EQ(valueAsBits(-1.0), valueAsBits(__llvm_libc::round(-0.715)));
+}
+
+TEST(RoundTest, InDoubleRange) {
+  using BitsType = Properties::BitsType;
+  constexpr BitsType count = 1000000;
+  constexpr BitsType step = UINT64_MAX / count;
+  for (BitsType i = 0, v = 0; i <= count; ++i, v += step) {
+    double x = valueFromBits(v);
+    if (isnan(x) || isinf(x))
+      continue;
+    ASSERT_MPFR_MATCH(mpfr::Operation::Round, x, __llvm_libc::round(x),
+                      tolerance);
+  }
+}
diff --git a/libc/test/src/math/roundf_test.cpp b/libc/test/src/math/roundf_test.cpp
new file mode 100644 (file)
index 0000000..88c746b
--- /dev/null
@@ -0,0 +1,85 @@
+//===-- Unittests for roundf ----------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "include/math.h"
+#include "src/math/roundf.h"
+#include "utils/FPUtil/BitPatterns.h"
+#include "utils/FPUtil/FloatOperations.h"
+#include "utils/FPUtil/FloatProperties.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include "utils/UnitTest/Test.h"
+
+using __llvm_libc::fputil::valueAsBits;
+using __llvm_libc::fputil::valueFromBits;
+
+using BitPatterns = __llvm_libc::fputil::BitPatterns<float>;
+using Properties = __llvm_libc::fputil::FloatProperties<float>;
+
+namespace mpfr = __llvm_libc::testing::mpfr;
+
+// Zero tolerance; As in, exact match with MPFR result.
+static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::doublePrecision, 0,
+                                           0};
+
+TEST(RoundfTest, SpecialNumbers) {
+  EXPECT_EQ(
+      BitPatterns::aQuietNaN,
+      valueAsBits(__llvm_libc::roundf(valueFromBits(BitPatterns::aQuietNaN))));
+  EXPECT_EQ(BitPatterns::aNegativeQuietNaN,
+            valueAsBits(__llvm_libc::roundf(
+                valueFromBits(BitPatterns::aNegativeQuietNaN))));
+
+  EXPECT_EQ(BitPatterns::aSignallingNaN,
+            valueAsBits(__llvm_libc::roundf(
+                valueFromBits(BitPatterns::aSignallingNaN))));
+  EXPECT_EQ(BitPatterns::aNegativeSignallingNaN,
+            valueAsBits(__llvm_libc::roundf(
+                valueFromBits(BitPatterns::aNegativeSignallingNaN))));
+
+  EXPECT_EQ(BitPatterns::inf,
+            valueAsBits(__llvm_libc::roundf(valueFromBits(BitPatterns::inf))));
+  EXPECT_EQ(BitPatterns::negInf, valueAsBits(__llvm_libc::roundf(
+                                     valueFromBits(BitPatterns::negInf))));
+
+  EXPECT_EQ(BitPatterns::zero,
+            valueAsBits(__llvm_libc::roundf(valueFromBits(BitPatterns::zero))));
+  EXPECT_EQ(BitPatterns::negZero, valueAsBits(__llvm_libc::roundf(
+                                      valueFromBits(BitPatterns::negZero))));
+}
+
+TEST(RoundfTest, RoundedNumbers) {
+  EXPECT_EQ(valueAsBits(1.0f), valueAsBits(__llvm_libc::roundf(1.0f)));
+  EXPECT_EQ(valueAsBits(-1.0f), valueAsBits(__llvm_libc::roundf(-1.0f)));
+  EXPECT_EQ(valueAsBits(10.0f), valueAsBits(__llvm_libc::roundf(10.0f)));
+  EXPECT_EQ(valueAsBits(-10.0f), valueAsBits(__llvm_libc::roundf(-10.0f)));
+  EXPECT_EQ(valueAsBits(12345.0f), valueAsBits(__llvm_libc::roundf(12345.0f)));
+  EXPECT_EQ(valueAsBits(-12345.0f),
+            valueAsBits(__llvm_libc::roundf(-12345.0f)));
+}
+
+TEST(RoundTest, CloseToZeroNumbers) {
+  EXPECT_EQ(valueAsBits(1.0f), valueAsBits(__llvm_libc::roundf(0.5f)));
+  EXPECT_EQ(valueAsBits(-1.0f), valueAsBits(__llvm_libc::roundf(-0.5f)));
+  EXPECT_EQ(valueAsBits(0.0f), valueAsBits(__llvm_libc::roundf(0.115f)));
+  EXPECT_EQ(valueAsBits(-0.0f), valueAsBits(__llvm_libc::roundf(-0.115f)));
+  EXPECT_EQ(valueAsBits(1.0f), valueAsBits(__llvm_libc::roundf(0.715f)));
+  EXPECT_EQ(valueAsBits(-1.0f), valueAsBits(__llvm_libc::roundf(-0.715f)));
+}
+
+TEST(RoundfTest, InFloatRange) {
+  using BitsType = Properties::BitsType;
+  constexpr BitsType count = 1000000;
+  constexpr BitsType step = UINT32_MAX / count;
+  for (BitsType i = 0, v = 0; i <= count; ++i, v += step) {
+    double x = valueFromBits(v);
+    if (isnan(x) || isinf(x))
+      continue;
+    ASSERT_MPFR_MATCH(mpfr::Operation::Round, x, __llvm_libc::roundf(x),
+                      tolerance);
+  }
+}
index a378903..d841237 100644 (file)
@@ -214,6 +214,62 @@ static inline T floor(T x) {
   }
 }
 
+template <typename T,
+          cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
+static inline T round(T x) {
+  using Properties = FloatProperties<T>;
+  using BitsType = typename FloatProperties<T>::BitsType;
+
+  BitsType bits = valueAsBits(x);
+
+  // If x is infinity, NaN or zero, return it.
+  if (bitsAreInfOrNaN(bits) || bitsAreZero(bits))
+    return x;
+
+  bool isNeg = bits & Properties::signMask;
+  int exponent = getExponentFromBits(bits);
+
+  // If the exponent is greater than the most negative mantissa
+  // exponent, then x is already an integer.
+  if (exponent >= static_cast<int>(Properties::mantissaWidth))
+    return x;
+
+  if (exponent == -1) {
+    // Absolute value of x is greater than equal to 0.5 but less than 1.
+    if (isNeg)
+      return T(-1.0);
+    else
+      return T(1.0);
+  }
+
+  if (exponent <= -2) {
+    // Absolute value of x is less than 0.5.
+    if (isNeg)
+      return T(-0.0);
+    else
+      return T(0.0);
+  }
+
+  uint32_t trimSize = Properties::mantissaWidth - exponent;
+  // If x is already an integer, return it.
+  if ((bits << (Properties::bitWidth - trimSize)) == 0)
+    return x;
+
+  BitsType truncBits = (bits >> trimSize) << trimSize;
+  T truncValue = valueFromBits(truncBits);
+
+  if ((bits & (BitsType(1) << (trimSize - 1))) == 0) {
+    // Franctional part is less than 0.5 so round value is the
+    // same as the trunc value.
+    return truncValue;
+  }
+
+  if (isNeg)
+    return truncValue - T(1.0);
+  else
+    return truncValue + T(1.0);
+}
+
 } // namespace fputil
 } // namespace __llvm_libc