[libc-commits] [libc] 96d8572 - [libc] Move implementations of expf and exp2f from the AOR to src/math.
Siva Chandra Reddy via libc-commits
libc-commits at lists.llvm.org
Fri May 15 12:52:19 PDT 2020
Author: Siva Chandra Reddy
Date: 2020-05-15T12:43:03-07:00
New Revision: 96d85726b0fc3c5154265cea156483a3f63ebe21
URL: https://github.com/llvm/llvm-project/commit/96d85726b0fc3c5154265cea156483a3f63ebe21
DIFF: https://github.com/llvm/llvm-project/commit/96d85726b0fc3c5154265cea156483a3f63ebe21.diff
LOG: [libc] Move implementations of expf and exp2f from the AOR to src/math.
Reviewers: phosek
Differential Revision: https://reviews.llvm.org/D79149
Added:
libc/src/math/exp2f.cpp
libc/src/math/exp2f.h
libc/src/math/exp_utils.cpp
libc/src/math/exp_utils.h
libc/src/math/expf.cpp
libc/src/math/expf.h
libc/src/math/math_utils.cpp
libc/test/src/math/exp2f_test.cpp
libc/test/src/math/expf_test.cpp
Modified:
libc/config/linux/api.td
libc/lib/CMakeLists.txt
libc/spec/stdc.td
libc/src/math/CMakeLists.txt
libc/src/math/cosf.cpp
libc/src/math/math_utils.h
libc/src/math/sincosf.cpp
libc/src/math/sinf.cpp
libc/test/src/math/CMakeLists.txt
libc/utils/MPFRWrapper/MPFRUtils.cpp
libc/utils/MPFRWrapper/MPFRUtils.h
Removed:
################################################################################
diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 3eb7ead48365..cb070e1466ad 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -152,6 +152,8 @@ def MathAPI : PublicAPI<"math.h"> {
"cosf",
"fabs",
"fabsf",
+ "expf",
+ "exp2f",
"round",
"sincosf",
"sinf",
diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt
index 2e103ee3ecf4..55f213a449b5 100644
--- a/libc/lib/CMakeLists.txt
+++ b/libc/lib/CMakeLists.txt
@@ -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
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 102f878d2fe0..139a1af84c58 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -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>]>,
]
>;
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index f74577ba85a1..41a20b31996f 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -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
+)
diff --git a/libc/src/math/cosf.cpp b/libc/src/math/cosf.cpp
index db121b2cb396..f02e38bee133 100644
--- a/libc/src/math/cosf.cpp
+++ b/libc/src/math/cosf.cpp
@@ -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
diff --git a/libc/src/math/exp2f.cpp b/libc/src/math/exp2f.cpp
new file mode 100644
index 000000000000..3d7423d0210b
--- /dev/null
+++ b/libc/src/math/exp2f.cpp
@@ -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
diff --git a/libc/src/math/exp2f.h b/libc/src/math/exp2f.h
new file mode 100644
index 000000000000..c1f72b1b80fa
--- /dev/null
+++ b/libc/src/math/exp2f.h
@@ -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
diff --git a/libc/src/math/exp_utils.cpp b/libc/src/math/exp_utils.cpp
new file mode 100644
index 000000000000..7635f44f1577
--- /dev/null
+++ b/libc/src/math/exp_utils.cpp
@@ -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
diff --git a/libc/src/math/exp_utils.h b/libc/src/math/exp_utils.h
new file mode 100644
index 000000000000..edbd60a2d568
--- /dev/null
+++ b/libc/src/math/exp_utils.h
@@ -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
diff --git a/libc/src/math/expf.cpp b/libc/src/math/expf.cpp
new file mode 100644
index 000000000000..beda61456b3a
--- /dev/null
+++ b/libc/src/math/expf.cpp
@@ -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
diff --git a/libc/src/math/expf.h b/libc/src/math/expf.h
new file mode 100644
index 000000000000..c61ce98e9ddc
--- /dev/null
+++ b/libc/src/math/expf.h
@@ -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
diff --git a/libc/src/math/math_utils.cpp b/libc/src/math/math_utils.cpp
new file mode 100644
index 000000000000..42a912bac769
--- /dev/null
+++ b/libc/src/math/math_utils.cpp
@@ -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
diff --git a/libc/src/math/math_utils.h b/libc/src/math/math_utils.h
index 3553673486f9..50851bd36f79 100644
--- a/libc/src/math/math_utils.h
+++ b/libc/src/math/math_utils.h
@@ -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
diff --git a/libc/src/math/sincosf.cpp b/libc/src/math/sincosf.cpp
index 717feaa470d2..710d7b4ffa06 100644
--- a/libc/src/math/sincosf.cpp
+++ b/libc/src/math/sincosf.cpp
@@ -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);
}
}
diff --git a/libc/src/math/sinf.cpp b/libc/src/math/sinf.cpp
index 634e40c50f6b..ea15c474ecd5 100644
--- a/libc/src/math/sinf.cpp
+++ b/libc/src/math/sinf.cpp
@@ -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
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 3ea485c34301..ed36faaee4d8 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -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
+)
diff --git a/libc/test/src/math/exp2f_test.cpp b/libc/test/src/math/exp2f_test.cpp
new file mode 100644
index 000000000000..dbb7046e28bd
--- /dev/null
+++ b/libc/test/src/math/exp2f_test.cpp
@@ -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);
+ }
+}
diff --git a/libc/test/src/math/expf_test.cpp b/libc/test/src/math/expf_test.cpp
new file mode 100644
index 000000000000..aa50bd71974b
--- /dev/null
+++ b/libc/test/src/math/expf_test.cpp
@@ -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);
+ }
+}
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index fe84c381114e..74c2f760f034 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -95,6 +95,12 @@ class MPFRNumber {
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;
}
}
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h
index f94a146a35f2..f6660f2fa78e 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.h
+++ b/libc/utils/MPFRWrapper/MPFRUtils.h
@@ -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 {
More information about the libc-commits
mailing list