[libc] Add implementations of round and roundf.

Reviewers: asteinhauser

Differential Revision: https://reviews.llvm.org/D80779
This commit is contained in:
Siva Chandra Reddy 2020-05-28 23:03:32 -07:00
parent 9ec57cce62
commit 4be1c116ad
12 changed files with 299 additions and 36 deletions

View file

@ -160,6 +160,7 @@ def MathAPI : PublicAPI<"math.h"> {
"expf",
"exp2f",
"round",
"roundf",
"sincosf",
"sinf",
"trunc",

View 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

View file

@ -9,9 +9,3 @@ add_entrypoint_library(
DEPENDS
${TARGET_LIBM_ENTRYPOINTS}
)
add_redirector_library(
llvmlibc_redirectors
DEPENDS
round_redirector
)

View 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>]>,

View file

@ -20,21 +20,6 @@ add_object_library(
.math_utils
)
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
@ -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

View 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

View 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

18
libc/src/math/roundf.h Normal file
View file

@ -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

View file

@ -175,6 +175,32 @@ add_math_unittest(
libc.utils.FPUtil.fputil
)
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

View file

@ -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);
}
}

View file

@ -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);
}
}

View 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