[libc] Move implementations of expf and exp2f from the AOR to src/math.

Reviewers: phosek

Differential Revision: https://reviews.llvm.org/D79149
This commit is contained in:
Siva Chandra Reddy
2020-05-15 12:43:03 -07:00
parent 0d5d5a75e2
commit 96d85726b0
20 changed files with 812 additions and 22 deletions
+2
View File
@@ -152,6 +152,8 @@ def MathAPI : PublicAPI<"math.h"> {
"cosf",
"fabs",
"fabsf",
"expf",
"exp2f",
"round",
"sincosf",
"sinf",
+2
View File
@@ -50,6 +50,8 @@ add_entrypoint_library(
libc.src.math.cosf
libc.src.math.fabs
libc.src.math.fabsf
libc.src.math.expf
libc.src.math.exp2f
libc.src.math.round
libc.src.math.sincosf
libc.src.math.sinf
+3
View File
@@ -188,6 +188,9 @@ def StdC : StandardSpec<"stdc"> {
FunctionSpec<"cosf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
FunctionSpec<"sinf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
FunctionSpec<"expf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
FunctionSpec<"exp2f", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
FunctionSpec<"round", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
]
>;
+37 -1
View File
@@ -1,5 +1,7 @@
add_header_library(
add_object_library(
math_utils
SRCS
math_utils.cpp
HDRS
math_utils.h
DEPENDS
@@ -88,3 +90,37 @@ add_entrypoint_object(
DEPENDS
libc.utils.FPUtil.fputil
)
add_object_library(
exp_utils
HDRS
exp_utils.h
SRCS
exp_utils.cpp
DEPENDS
.math_utils
)
add_entrypoint_object(
expf
SRCS
expf.cpp
HDRS
expf.h
DEPENDS
.exp_utils
.math_utils
libc.include.math
)
add_entrypoint_object(
exp2f
SRCS
exp2f.cpp
HDRS
exp2f.h
DEPENDS
.exp_utils
.math_utils
libc.include.math
)
+1 -1
View File
@@ -58,7 +58,7 @@ float LLVM_LIBC_ENTRYPOINT(cosf)(float y) {
return sinf_poly(x * s, x * x, p, n ^ 1);
}
return invalidf(y);
return invalid(y);
}
} // namespace __llvm_libc
+63
View File
@@ -0,0 +1,63 @@
//===-- Single-precision 2^x function -------------------------------------===//
//
// 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 "exp_utils.h"
#include "math_utils.h"
#include "include/math.h"
#include "src/__support/common.h"
#include <stdint.h>
#define T exp2f_data.tab
#define C exp2f_data.poly
#define SHIFT exp2f_data.shift_scaled
namespace __llvm_libc {
float LLVM_LIBC_ENTRYPOINT(exp2f)(float x) {
uint32_t abstop;
uint64_t ki, t;
// double_t for better performance on targets with FLT_EVAL_METHOD==2.
double_t kd, xd, z, r, r2, y, s;
xd = static_cast<double_t>(x);
abstop = top12_bits(x) & 0x7ff;
if (unlikely(abstop >= top12_bits(128.0f))) {
// |x| >= 128 or x is nan.
if (as_uint32_bits(x) == as_uint32_bits(-INFINITY))
return 0.0f;
if (abstop >= top12_bits(INFINITY))
return x + x;
if (x > 0.0f)
return overflow<float>(0);
if (x <= -150.0f)
return underflow<float>(0);
if (x < -149.0f)
return may_underflow<float>(0);
}
// x = k/N + r with r in [-1/(2N), 1/(2N)] and int k.
kd = static_cast<double>(xd + SHIFT);
ki = as_uint64_bits(kd);
kd -= SHIFT; // k/N for int k.
r = xd - kd;
// exp2(x) = 2^(k/N) * 2^r ~= s * (C0*r^3 + C1*r^2 + C2*r + 1)
t = T[ki % N];
t += ki << (52 - EXP2F_TABLE_BITS);
s = as_double(t);
z = C[0] * r + C[1];
r2 = r * r;
y = C[2] * r + 1;
y = z * r2 + y;
y = y * s;
return static_cast<float>(y);
}
} // namespace __llvm_libc
+18
View File
@@ -0,0 +1,18 @@
//===-- Implementation header for exp2f -------------------------*- 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_EXP2F_H
#define LLVM_LIBC_SRC_MATH_EXP2F_H
namespace __llvm_libc {
float exp2f(float x);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_MATH_EXP2F_H
+129
View File
@@ -0,0 +1,129 @@
//===-- Implemention of exp and friends' utils ----------------------------===//
//
// 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 "exp_utils.h"
#include "math_utils.h"
namespace __llvm_libc {
const Exp2fDataTable exp2f_data = {
// :tab[i] = uint(2^(i/N)) - (i << 52-BITS)
// used for computing 2^(k/N) for an int |k| < 150 N as
// double(tab[k%N] + (k << 52-BITS))
{
// tab
#if N == 8
0x3ff0000000000000,
0x3fef72b83c7d517b,
0x3fef06fe0a31b715,
0x3feebfdad5362a27,
0x3feea09e667f3bcd,
0x3feeace5422aa0db,
0x3feee89f995ad3ad,
0x3fef5818dcfba487,
#elif N == 16
0x3ff0000000000000,
0x3fefb5586cf9890f,
0x3fef72b83c7d517b,
0x3fef387a6e756238,
0x3fef06fe0a31b715,
0x3feedea64c123422,
0x3feebfdad5362a27,
0x3feeab07dd485429,
0x3feea09e667f3bcd,
0x3feea11473eb0187,
0x3feeace5422aa0db,
0x3feec49182a3f090,
0x3feee89f995ad3ad,
0x3fef199bdd85529c,
0x3fef5818dcfba487,
0x3fefa4afa2a490da,
#elif N == 32
0x3ff0000000000000, 0x3fefd9b0d3158574, 0x3fefb5586cf9890f,
0x3fef9301d0125b51, 0x3fef72b83c7d517b, 0x3fef54873168b9aa,
0x3fef387a6e756238, 0x3fef1e9df51fdee1, 0x3fef06fe0a31b715,
0x3feef1a7373aa9cb, 0x3feedea64c123422, 0x3feece086061892d,
0x3feebfdad5362a27, 0x3feeb42b569d4f82, 0x3feeab07dd485429,
0x3feea47eb03a5585, 0x3feea09e667f3bcd, 0x3fee9f75e8ec5f74,
0x3feea11473eb0187, 0x3feea589994cce13, 0x3feeace5422aa0db,
0x3feeb737b0cdc5e5, 0x3feec49182a3f090, 0x3feed503b23e255d,
0x3feee89f995ad3ad, 0x3feeff76f2fb5e47, 0x3fef199bdd85529c,
0x3fef3720dcef9069, 0x3fef5818dcfba487, 0x3fef7c97337b9b5f,
0x3fefa4afa2a490da, 0x3fefd0765b6e4540,
#elif N == 64
0x3ff0000000000000, 0x3fefec9a3e778061, 0x3fefd9b0d3158574,
0x3fefc74518759bc8, 0x3fefb5586cf9890f, 0x3fefa3ec32d3d1a2,
0x3fef9301d0125b51, 0x3fef829aaea92de0, 0x3fef72b83c7d517b,
0x3fef635beb6fcb75, 0x3fef54873168b9aa, 0x3fef463b88628cd6,
0x3fef387a6e756238, 0x3fef2b4565e27cdd, 0x3fef1e9df51fdee1,
0x3fef1285a6e4030b, 0x3fef06fe0a31b715, 0x3feefc08b26416ff,
0x3feef1a7373aa9cb, 0x3feee7db34e59ff7, 0x3feedea64c123422,
0x3feed60a21f72e2a, 0x3feece086061892d, 0x3feec6a2b5c13cd0,
0x3feebfdad5362a27, 0x3feeb9b2769d2ca7, 0x3feeb42b569d4f82,
0x3feeaf4736b527da, 0x3feeab07dd485429, 0x3feea76f15ad2148,
0x3feea47eb03a5585, 0x3feea23882552225, 0x3feea09e667f3bcd,
0x3fee9fb23c651a2f, 0x3fee9f75e8ec5f74, 0x3fee9feb564267c9,
0x3feea11473eb0187, 0x3feea2f336cf4e62, 0x3feea589994cce13,
0x3feea8d99b4492ed, 0x3feeace5422aa0db, 0x3feeb1ae99157736,
0x3feeb737b0cdc5e5, 0x3feebd829fde4e50, 0x3feec49182a3f090,
0x3feecc667b5de565, 0x3feed503b23e255d, 0x3feede6b5579fdbf,
0x3feee89f995ad3ad, 0x3feef3a2b84f15fb, 0x3feeff76f2fb5e47,
0x3fef0c1e904bc1d2, 0x3fef199bdd85529c, 0x3fef27f12e57d14b,
0x3fef3720dcef9069, 0x3fef472d4a07897c, 0x3fef5818dcfba487,
0x3fef69e603db3285, 0x3fef7c97337b9b5f, 0x3fef902ee78b3ff6,
0x3fefa4afa2a490da, 0x3fefba1bee615a27, 0x3fefd0765b6e4540,
0x3fefe7c1819e90d8,
#endif
},
as_double(0x4338000000000000) / N, // shift_scaled
{
// poly
#if N == 8
as_double(0x3fac6a00335106e2),
as_double(0x3fcec0c313449f55),
as_double(0x3fe62e431111f69f),
#elif N == 16
as_double(0x3fac6ac6aa313963),
as_double(0x3fcebfff4532d9ba),
as_double(0x3fe62e43001bc49f),
#elif N == 32
as_double(0x3fac6af84b912394),
as_double(0x3fcebfce50fac4f3),
as_double(0x3fe62e42ff0c52d6),
#elif N == 64
as_double(0x3fac6b04b4221b2a),
as_double(0x3fcebfc213e184d7),
as_double(0x3fe62e42fefb5b7f),
#endif
},
as_double(0x4338000000000000), // shift
as_double(0x3ff71547652b82fe) * N, // invln2_scaled
{
// poly_scaled
#if N == 8
as_double(0x3fac6a00335106e2) / N / N / N,
as_double(0x3fcec0c313449f55) / N / N,
as_double(0x3fe62e431111f69f) / N,
#elif N == 16
as_double(0x3fac6ac6aa313963) / N / N / N,
as_double(0x3fcebfff4532d9ba) / N / N,
as_double(0x3fe62e43001bc49f) / N,
#elif N == 32
as_double(0x3fac6af84b912394) / N / N / N,
as_double(0x3fcebfce50fac4f3) / N / N,
as_double(0x3fe62e42ff0c52d6) / N,
#elif N == 64
as_double(0x3fac6b04b4221b2a) / N / N / N,
as_double(0x3fcebfc213e184d7) / N / N,
as_double(0x3fe62e42fefb5b7f) / N,
#endif
},
};
} // namespace __llvm_libc
+33
View File
@@ -0,0 +1,33 @@
//===-- Collection of utils for exp and friends -----------------*- 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_EXP_UTILS_H
#define LLVM_LIBC_SRC_MATH_EXP_UTILS_H
#include <stdint.h>
#define EXP2F_TABLE_BITS 5
#define EXP2F_POLY_ORDER 3
#define N (1 << EXP2F_TABLE_BITS)
namespace __llvm_libc {
struct Exp2fDataTable {
uint64_t tab[1 << EXP2F_TABLE_BITS];
double shift_scaled;
double poly[EXP2F_POLY_ORDER];
double shift;
double invln2_scaled;
double poly_scaled[EXP2F_POLY_ORDER];
};
extern const Exp2fDataTable exp2f_data;
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_MATH_EXP_UTILS_H
+69
View File
@@ -0,0 +1,69 @@
//===-- Single-precision e^x function -------------------------------------===//
//
// 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 "exp_utils.h"
#include "math_utils.h"
#include "include/math.h"
#include "src/__support/common.h"
#include <stdint.h>
#define InvLn2N exp2f_data.invln2_scaled
#define T exp2f_data.tab
#define C exp2f_data.poly_scaled
#define SHIFT exp2f_data.shift
namespace __llvm_libc {
float LLVM_LIBC_ENTRYPOINT(expf)(float x) {
uint32_t abstop;
uint64_t ki, t;
// double_t for better performance on targets with FLT_EVAL_METHOD == 2.
double_t kd, xd, z, r, r2, y, s;
xd = static_cast<double_t>(x);
abstop = top12_bits(x) & 0x7ff;
if (unlikely(abstop >= top12_bits(88.0f))) {
// |x| >= 88 or x is nan.
if (as_uint32_bits(x) == as_uint32_bits(-INFINITY))
return 0.0f;
if (abstop >= top12_bits(INFINITY))
return x + x;
if (x > as_float(0x42b17217)) // x > log(0x1p128) ~= 88.72
return overflow<float>(0);
if (x < as_float(0xc2cff1b4)) // x < log(0x1p-150) ~= -103.97
return underflow<float>(0);
if (x < as_float(0xc2ce8ecf)) // x < log(0x1p-149) ~= -103.28
return may_underflow<float>(0);
}
// x*N/Ln2 = k + r with r in [-1/2, 1/2] and int k.
z = InvLn2N * xd;
// Round and convert z to int, the result is in [-150*N, 128*N] and
// ideally nearest int is used, otherwise the magnitude of r can be
// bigger which gives larger approximation error.
kd = static_cast<double>(z + SHIFT);
ki = as_uint64_bits(kd);
kd -= SHIFT;
r = z - kd;
// exp(x) = 2^(k/N) * 2^(r/N) ~= s *(C0*r^3 + C1*r^2 + C2*r + 1)
t = T[ki % N];
t += ki << (52 - EXP2F_TABLE_BITS);
s = as_double(t);
z = C[0] * r + C[1];
r2 = r * r;
y = C[2] * r + 1;
y = z * r2 + y;
y = y * s;
return static_cast<float>(y);
}
} // namespace __llvm_libc
+18
View File
@@ -0,0 +1,18 @@
//===-- Implementation header for expf --------------------------*- 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_EXPF_H
#define LLVM_LIBC_SRC_MATH_EXPF_H
namespace __llvm_libc {
float expf(float x);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_MATH_EXPF_H
+27
View File
@@ -0,0 +1,27 @@
//===-- Implementation of math utils --------------------------------------===//
//
// 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 "math_utils.h"
namespace __llvm_libc {
const float XFlowValues<float>::overflow_value =
as_float(0x70000000); // 0x1p97f
const float XFlowValues<float>::underflow_value =
as_float(0x10000000); // 0x1p97f
const float XFlowValues<float>::may_underflow_value =
as_float(0x1a200000); // 0x1.4p-75f
const double XFlowValues<double>::overflow_value =
as_double(0x7000000000000000); // 0x1p769
const double XFlowValues<double>::underflow_value =
as_double(0x1000000000000000); // 0x1p-767
const double XFlowValues<double>::may_underflow_value =
as_double(0x1e58000000000000); // 0x1.8p-538
} // namespace __llvm_libc
+71 -11
View File
@@ -11,24 +11,22 @@
#include "include/errno.h"
#include "include/math.h"
#include "src/__support/common.h"
#include "src/errno/llvmlibc_errno.h"
#include "utils/CPP/TypeTraits.h"
#include <stdint.h>
namespace __llvm_libc {
static inline float with_errnof(float x, int err) {
if (math_errhandling & MATH_ERRNO)
llvmlibc_errno = err;
return x;
}
static inline uint32_t as_uint32_bits(float x) {
return *reinterpret_cast<uint32_t *>(&x);
}
static inline uint64_t as_uint64_bits(double x) {
return *reinterpret_cast<uint64_t *>(&x);
}
static inline float as_float(uint32_t x) {
return *reinterpret_cast<float *>(&x);
}
@@ -37,12 +35,74 @@ static inline double as_double(uint64_t x) {
return *reinterpret_cast<double *>(&x);
}
static inline constexpr float invalidf(float x) {
float y = (x - x) / (x - x);
return isnan(x) ? y : with_errnof(y, EDOM);
static inline uint32_t top12_bits(float x) { return as_uint32_bits(x) >> 20; }
static inline uint32_t top12_bits(double x) { return as_uint64_bits(x) >> 52; }
// Values to trigger underflow and overflow.
template <typename T> struct XFlowValues;
template <> struct XFlowValues<float> {
static const float overflow_value;
static const float underflow_value;
static const float may_underflow_value;
};
template <> struct XFlowValues<double> {
static const double overflow_value;
static const double underflow_value;
static const double may_underflow_value;
};
template <typename T> static inline T with_errno(T x, int err) {
if (math_errhandling & MATH_ERRNO)
llvmlibc_errno = err;
return x;
}
static inline void force_eval_float(float x) { volatile float y UNUSED = x; }
template <typename T> static inline void force_eval(T x) {
volatile T y UNUSED = x;
}
template <typename T> static inline T opt_barrier(T x) {
volatile T y = x;
return y;
}
template <typename T> struct IsFloatOrDouble {
static constexpr bool Value =
cpp::IsSame<T, float>::Value || cpp::IsSame<T, double>::Value;
};
template <typename T>
using EnableIfFloatOrDouble = cpp::EnableIfType<IsFloatOrDouble<T>::Value, int>;
template <typename T, EnableIfFloatOrDouble<T> = 0>
T xflow(uint32_t sign, T y) {
// Underflow happens when two extremely small values are multiplied.
// Likewise, overflow happens when two large values are mulitplied.
y = opt_barrier(sign ? -y : y) * y;
return with_errno(y, ERANGE);
}
template <typename T, EnableIfFloatOrDouble<T> = 0> T overflow(uint32_t sign) {
return xflow(sign, XFlowValues<T>::overflow_value);
}
template <typename T, EnableIfFloatOrDouble<T> = 0> T underflow(uint32_t sign) {
return xflow(sign, XFlowValues<T>::underflow_value);
}
template <typename T, EnableIfFloatOrDouble<T> = 0>
T may_underflow(uint32_t sign) {
return xflow(sign, XFlowValues<T>::may_underflow_value);
}
template <typename T, EnableIfFloatOrDouble<T> = 0>
static inline constexpr float invalid(T x) {
T y = (x - x) / (x - x);
return isnan(x) ? y : with_errno(y, EDOM);
}
} // namespace __llvm_libc
+2 -2
View File
@@ -32,7 +32,7 @@ void LLVM_LIBC_ENTRYPOINT(sincosf)(float y, float *sinp, float *cosp) {
if (unlikely(abstop12(y) < abstop12(as_float(0x39800000)))) {
if (unlikely(abstop12(y) < abstop12(as_float(0x800000))))
// Force underflow for tiny y.
force_eval_float(x2);
force_eval<float>(x2);
*sinp = y;
*cosp = 1.0f;
return;
@@ -69,7 +69,7 @@ void LLVM_LIBC_ENTRYPOINT(sincosf)(float y, float *sinp, float *cosp) {
// Needed to set errno for +-Inf, the add is a hack to work
// around a gcc register allocation issue: just passing y
// affects code generation in the fast path.
invalidf(y + y);
invalid(y + y);
}
}
+2 -2
View File
@@ -32,7 +32,7 @@ float LLVM_LIBC_ENTRYPOINT(sinf)(float y) {
if (unlikely(abstop12(y) < abstop12(as_float(0x39800000)))) {
if (unlikely(abstop12(y) < abstop12(as_float(0x800000))))
// Force underflow for tiny y.
force_eval_float(s);
force_eval<float>(s);
return y;
}
@@ -62,7 +62,7 @@ float LLVM_LIBC_ENTRYPOINT(sinf)(float y) {
return sinf_poly(x * s, x * x, p, n);
}
return invalidf(y);
return invalid(y);
}
} // namespace __llvm_libc
+28
View File
@@ -96,3 +96,31 @@ add_math_unittest(
libc.src.math.fabsf
libc.utils.FPUtil.fputil
)
add_math_unittest(
expf_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
expf_test.cpp
DEPENDS
libc.include.errno
libc.include.math
libc.src.math.expf
libc.utils.FPUtil.fputil
)
add_math_unittest(
exp2f_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
exp2f_test.cpp
DEPENDS
libc.include.errno
libc.include.math
libc.src.math.exp2f
libc.utils.FPUtil.fputil
)
+154
View File
@@ -0,0 +1,154 @@
//===-- Unittests for exp2f -----------------------------------------------===//
//
// 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/errno.h"
#include "include/math.h"
#include "src/errno/llvmlibc_errno.h"
#include "src/math/exp2f.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"
#include <stdint.h>
using __llvm_libc::fputil::isNegativeQuietNaN;
using __llvm_libc::fputil::isQuietNaN;
using __llvm_libc::fputil::valueAsBits;
using __llvm_libc::fputil::valueFromBits;
using BitPatterns = __llvm_libc::fputil::BitPatterns<float>;
namespace mpfr = __llvm_libc::testing::mpfr;
// 12 additional bits of precision over the base precision of a |float|
// value.
static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12,
0xFFF};
TEST(exp2fTest, SpecialNumbers) {
llvmlibc_errno = 0;
EXPECT_TRUE(
isQuietNaN(__llvm_libc::exp2f(valueFromBits(BitPatterns::aQuietNaN))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_TRUE(isNegativeQuietNaN(
__llvm_libc::exp2f(valueFromBits(BitPatterns::aNegativeQuietNaN))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_TRUE(isQuietNaN(
__llvm_libc::exp2f(valueFromBits(BitPatterns::aSignallingNaN))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_TRUE(isNegativeQuietNaN(
__llvm_libc::exp2f(valueFromBits(BitPatterns::aNegativeSignallingNaN))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_EQ(BitPatterns::inf,
valueAsBits(__llvm_libc::exp2f(valueFromBits(BitPatterns::inf))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_EQ(BitPatterns::zero, valueAsBits(__llvm_libc::exp2f(
valueFromBits(BitPatterns::negInf))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_EQ(BitPatterns::one,
valueAsBits(__llvm_libc::exp2f(valueFromBits(BitPatterns::zero))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_EQ(BitPatterns::one, valueAsBits(__llvm_libc::exp2f(
valueFromBits(BitPatterns::negZero))));
EXPECT_EQ(llvmlibc_errno, 0);
}
TEST(ExpfTest, Overflow) {
llvmlibc_errno = 0;
EXPECT_EQ(BitPatterns::inf,
valueAsBits(__llvm_libc::exp2f(valueFromBits(0x7f7fffffU))));
EXPECT_EQ(llvmlibc_errno, ERANGE);
llvmlibc_errno = 0;
EXPECT_EQ(BitPatterns::inf,
valueAsBits(__llvm_libc::exp2f(valueFromBits(0x43000000U))));
EXPECT_EQ(llvmlibc_errno, ERANGE);
llvmlibc_errno = 0;
EXPECT_EQ(BitPatterns::inf,
valueAsBits(__llvm_libc::exp2f(valueFromBits(0x43000001U))));
EXPECT_EQ(llvmlibc_errno, ERANGE);
}
// Test with inputs which are the borders of underflow/overflow but still
// produce valid results without setting errno.
TEST(ExpfTest, Borderline) {
float x;
llvmlibc_errno = 0;
x = valueFromBits(0x42fa0001U);
EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
EXPECT_EQ(llvmlibc_errno, 0);
x = valueFromBits(0x42ffffffU);
EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
EXPECT_EQ(llvmlibc_errno, 0);
x = valueFromBits(0xc2fa0001U);
EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
EXPECT_EQ(llvmlibc_errno, 0);
x = valueFromBits(0xc2fc0000U);
EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
EXPECT_EQ(llvmlibc_errno, 0);
x = valueFromBits(0xc2fc0001U);
EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
EXPECT_EQ(llvmlibc_errno, 0);
x = valueFromBits(0xc3150000U);
EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
EXPECT_EQ(llvmlibc_errno, 0);
}
TEST(ExpfTest, Underflow) {
llvmlibc_errno = 0;
EXPECT_EQ(BitPatterns::zero,
valueAsBits(__llvm_libc::exp2f(valueFromBits(0xff7fffffU))));
EXPECT_EQ(llvmlibc_errno, ERANGE);
llvmlibc_errno = 0;
float x = valueFromBits(0xc3158000U);
EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
EXPECT_EQ(llvmlibc_errno, ERANGE);
llvmlibc_errno = 0;
x = valueFromBits(0xc3165432U);
EXPECT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
EXPECT_EQ(llvmlibc_errno, ERANGE);
}
TEST(exp2fTest, InFloatRange) {
constexpr uint32_t count = 1000000;
constexpr uint32_t step = UINT32_MAX / count;
for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) {
float x = valueFromBits(v);
if (isnan(x) || isinf(x))
continue;
llvmlibc_errno = 0;
float result = __llvm_libc::exp2f(x);
// If the computation resulted in an error or did not produce valid result
// in the single-precision floating point range, then ignore comparing with
// MPFR result as MPFR can still produce valid results because of its
// wider precision.
if (isnan(result) || isinf(result) || llvmlibc_errno != 0)
continue;
ASSERT_MPFR_MATCH(mpfr::OP_Exp2, x, __llvm_libc::exp2f(x), tolerance);
}
}
+146
View File
@@ -0,0 +1,146 @@
//===-- Unittests for expf ------------------------------------------------===//
//
// 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/errno.h"
#include "include/math.h"
#include "src/errno/llvmlibc_errno.h"
#include "src/math/expf.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"
#include <stdint.h>
using __llvm_libc::fputil::isNegativeQuietNaN;
using __llvm_libc::fputil::isQuietNaN;
using __llvm_libc::fputil::valueAsBits;
using __llvm_libc::fputil::valueFromBits;
using BitPatterns = __llvm_libc::fputil::BitPatterns<float>;
namespace mpfr = __llvm_libc::testing::mpfr;
// 12 additional bits of precision over the base precision of a |float|
// value.
static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12,
0xFFF};
TEST(ExpfTest, SpecialNumbers) {
llvmlibc_errno = 0;
EXPECT_TRUE(
isQuietNaN(__llvm_libc::expf(valueFromBits(BitPatterns::aQuietNaN))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_TRUE(isNegativeQuietNaN(
__llvm_libc::expf(valueFromBits(BitPatterns::aNegativeQuietNaN))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_TRUE(isQuietNaN(
__llvm_libc::expf(valueFromBits(BitPatterns::aSignallingNaN))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_TRUE(isNegativeQuietNaN(
__llvm_libc::expf(valueFromBits(BitPatterns::aNegativeSignallingNaN))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_EQ(BitPatterns::inf,
valueAsBits(__llvm_libc::expf(valueFromBits(BitPatterns::inf))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_EQ(BitPatterns::zero,
valueAsBits(__llvm_libc::expf(valueFromBits(BitPatterns::negInf))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_EQ(BitPatterns::one,
valueAsBits(__llvm_libc::expf(valueFromBits(BitPatterns::zero))));
EXPECT_EQ(llvmlibc_errno, 0);
EXPECT_EQ(BitPatterns::one, valueAsBits(__llvm_libc::expf(
valueFromBits(BitPatterns::negZero))));
EXPECT_EQ(llvmlibc_errno, 0);
}
TEST(ExpfTest, Overflow) {
llvmlibc_errno = 0;
EXPECT_EQ(BitPatterns::inf,
valueAsBits(__llvm_libc::expf(valueFromBits(0x7f7fffffU))));
EXPECT_EQ(llvmlibc_errno, ERANGE);
llvmlibc_errno = 0;
EXPECT_EQ(BitPatterns::inf,
valueAsBits(__llvm_libc::expf(valueFromBits(0x42cffff8U))));
EXPECT_EQ(llvmlibc_errno, ERANGE);
llvmlibc_errno = 0;
EXPECT_EQ(BitPatterns::inf,
valueAsBits(__llvm_libc::expf(valueFromBits(0x42d00008U))));
EXPECT_EQ(llvmlibc_errno, ERANGE);
}
TEST(ExpfTest, Underflow) {
llvmlibc_errno = 0;
EXPECT_EQ(BitPatterns::zero,
valueAsBits(__llvm_libc::expf(valueFromBits(0xff7fffffU))));
EXPECT_EQ(llvmlibc_errno, ERANGE);
llvmlibc_errno = 0;
EXPECT_EQ(BitPatterns::zero,
valueAsBits(__llvm_libc::expf(valueFromBits(0xc2cffff8U))));
EXPECT_EQ(llvmlibc_errno, ERANGE);
llvmlibc_errno = 0;
EXPECT_EQ(BitPatterns::zero,
valueAsBits(__llvm_libc::expf(valueFromBits(0xc2d00008U))));
EXPECT_EQ(llvmlibc_errno, ERANGE);
}
// Test with inputs which are the borders of underflow/overflow but still
// produce valid results without setting errno.
TEST(ExpfTest, Borderline) {
float x;
llvmlibc_errno = 0;
x = valueFromBits(0x42affff8U);
ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance);
EXPECT_EQ(llvmlibc_errno, 0);
x = valueFromBits(0x42b00008U);
ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance);
EXPECT_EQ(llvmlibc_errno, 0);
x = valueFromBits(0xc2affff8U);
ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance);
EXPECT_EQ(llvmlibc_errno, 0);
x = valueFromBits(0xc2b00008U);
ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance);
EXPECT_EQ(llvmlibc_errno, 0);
}
TEST(ExpfTest, InFloatRange) {
constexpr uint32_t count = 1000000;
constexpr uint32_t step = UINT32_MAX / count;
for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) {
float x = valueFromBits(v);
if (isnan(x) || isinf(x))
continue;
llvmlibc_errno = 0;
float result = __llvm_libc::expf(x);
// If the computation resulted in an error or did not produce valid result
// in the single-precision floating point range, then ignore comparing with
// MPFR result as MPFR can still produce valid results because of its
// wider precision.
if (isnan(result) || isinf(result) || llvmlibc_errno != 0)
continue;
ASSERT_MPFR_MATCH(mpfr::OP_Exp, x, __llvm_libc::expf(x), tolerance);
}
}
+6
View File
@@ -95,6 +95,12 @@ public:
case OP_Sin:
mpfr_sin(value, mpfrInput.value, MPFR_RNDN);
break;
case OP_Exp:
mpfr_exp(value, mpfrInput.value, MPFR_RNDN);
break;
case OP_Exp2:
mpfr_exp2(value, mpfrInput.value, MPFR_RNDN);
break;
}
}
+1 -5
View File
@@ -39,11 +39,7 @@ struct Tolerance {
uint32_t bits;
};
enum Operation {
OP_Abs,
OP_Cos,
OP_Sin,
};
enum Operation { OP_Abs, OP_Cos, OP_Sin, OP_Exp, OP_Exp2 };
namespace internal {