Skip to content

Commit a7f55f0

Browse files
author
Kirill Okhotnikov
committed
[libc][math] Added sinhf function.
Differential Revision: https://reviews.llvm.org/D129278
1 parent fcb9d7e commit a7f55f0

File tree

15 files changed

+332
-0
lines changed

15 files changed

+332
-0
lines changed

libc/config/darwin/arm/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ set(TARGET_LIBM_ENTRYPOINTS
187187
libc.src.math.roundf
188188
libc.src.math.roundl
189189
libc.src.math.sincosf
190+
libc.src.math.sinhf
190191
libc.src.math.sinf
191192
libc.src.math.sqrt
192193
libc.src.math.sqrtf

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ set(TARGET_LIBM_ENTRYPOINTS
206206
libc.src.math.roundf
207207
libc.src.math.roundl
208208
libc.src.math.sincosf
209+
libc.src.math.sinhf
209210
libc.src.math.sinf
210211
libc.src.math.sqrt
211212
libc.src.math.sqrtf

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ set(TARGET_LIBM_ENTRYPOINTS
213213
libc.src.math.roundl
214214
libc.src.math.sin
215215
libc.src.math.sincosf
216+
libc.src.math.sinhf
216217
libc.src.math.sinf
217218
libc.src.math.sqrt
218219
libc.src.math.sqrtf

libc/config/windows/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ set(TARGET_LIBM_ENTRYPOINTS
191191
libc.src.math.sin
192192
libc.src.math.sincosf
193193
libc.src.math.sinf
194+
libc.src.math.sinhf
194195
libc.src.math.sqrt
195196
libc.src.math.sqrtf
196197
libc.src.math.sqrtl

libc/spec/stdc.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ def StdC : StandardSpec<"stdc"> {
472472
FunctionSpec<"nextafterl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
473473

474474
FunctionSpec<"coshf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
475+
FunctionSpec<"sinhf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
475476
]
476477
>;
477478

libc/src/math/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ add_math_entrypoint_object(sincosf)
182182

183183
add_math_entrypoint_object(sin)
184184
add_math_entrypoint_object(sinf)
185+
add_math_entrypoint_object(sinhf)
185186

186187
add_math_entrypoint_object(sqrt)
187188
add_math_entrypoint_object(sqrtf)

libc/src/math/generic/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,3 +1147,21 @@ add_entrypoint_object(
11471147
-O3
11481148
)
11491149

1150+
add_entrypoint_object(
1151+
sinhf
1152+
SRCS
1153+
sinhf.cpp
1154+
HDRS
1155+
../sinhf.h
1156+
expxf.h
1157+
DEPENDS
1158+
.common_constants
1159+
libc.src.__support.FPUtil.fputil
1160+
libc.src.__support.FPUtil.multiply_add
1161+
libc.src.__support.FPUtil.nearest_integer
1162+
libc.src.__support.FPUtil.polyeval
1163+
libc.include.math
1164+
COMPILE_OPTIONS
1165+
-O3
1166+
)
1167+

libc/src/math/generic/sinhf.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//===-- Single-precision sinh function ------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/math/sinhf.h"
10+
#include "src/__support/FPUtil/FPBits.h"
11+
#include "src/math/generic/expxf.h"
12+
13+
namespace __llvm_libc {
14+
15+
LLVM_LIBC_FUNCTION(float, sinhf, (float x)) {
16+
using FPBits = typename fputil::FPBits<float>;
17+
FPBits xbits(x);
18+
bool sign = xbits.get_sign();
19+
uint32_t x_abs = xbits.uintval() & FPBits::FloatProp::EXP_MANT_MASK;
20+
21+
// |x| <= 2^-26
22+
if (unlikely(x_abs <= 0x3280'0000U)) {
23+
return unlikely(x_abs == 0) ? x : (x + 0.25 * x * x * x);
24+
}
25+
26+
// When |x| >= 90, or x is inf or nan
27+
if (unlikely(x_abs >= 0x42b4'0000U)) {
28+
if (xbits.is_nan())
29+
return x + 1.0f; // sNaN to qNaN + signal
30+
31+
if (xbits.is_inf())
32+
return x;
33+
34+
int rounding = fputil::get_round();
35+
if (sign) {
36+
if (unlikely(rounding == FE_UPWARD || rounding == FE_TOWARDZERO))
37+
return FPBits(FPBits::MAX_NORMAL | FPBits::FloatProp::SIGN_MASK)
38+
.get_val();
39+
} else {
40+
if (unlikely(rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO))
41+
return FPBits(FPBits::MAX_NORMAL).get_val();
42+
}
43+
44+
errno = ERANGE;
45+
46+
return x + FPBits::inf(sign).get_val();
47+
}
48+
49+
// |x| <= 0.078125
50+
if (unlikely(x_abs <= 0x3da0'0000U)) {
51+
// |x| = 0.0005589424981735646724700927734375
52+
if (unlikely(x_abs == 0x3a12'85ffU)) {
53+
if (fputil::get_round() == FE_TONEAREST)
54+
return x;
55+
}
56+
57+
double xdbl = x;
58+
double x2 = xdbl * xdbl;
59+
// Sollya: fpminimax(sinh(x),[|3,5,7|],[|D...|],[-1/16-1/64;1/16+1/64],x);
60+
// Sollya output: x * (0x1p0 + x^0x1p1 * (0x1.5555555556583p-3 + x^0x1p1
61+
// * (0x1.111110d239f1fp-7
62+
// + x^0x1p1 * 0x1.a02b5a284013cp-13)))
63+
// Therefore, output of Sollya = x * pe;
64+
double pe = fputil::polyeval(x2, 0.0, 0x1.5555555556583p-3,
65+
0x1.111110d239f1fp-7, 0x1.a02b5a284013cp-13);
66+
return fputil::multiply_add(xdbl, pe, xdbl);
67+
}
68+
69+
// MULT_POWER2 = -1
70+
auto ep_p = exp_eval<-1>(x); // 0.5 * exp(x)
71+
auto ep_m = exp_eval<-1>(-x); // 0.5 * exp(-x)
72+
73+
// 0.5 * expm1(x) = ep_p.mult_exp * (ep_p.r + 1) - 0.5
74+
// = ep_p.mult_exp * ep_p.r + ep_p.mult_exp - 0.5
75+
// 0.5 * expm1(-x) = ep_m.mult_exp * (ep_m.r + 1) - 0.5
76+
// = ep_m.mult_exp * ep_m.r + ep_m.mult_exp - 0.5
77+
// sinh(x) = 0.5 * expm1(x) - 0.5 * expm1(-x)
78+
// Using expm1 instead of exp improved precision around zero.
79+
double ep = fputil::multiply_add(ep_p.mult_exp, ep_p.r, ep_p.mult_exp - 0.5) -
80+
fputil::multiply_add(ep_m.mult_exp, ep_m.r, ep_m.mult_exp - 0.5);
81+
return ep;
82+
}
83+
84+
} // namespace __llvm_libc

libc/src/math/sinhf.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//===-- Implementation header for sinhf -------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_MATH_SINHF_H
10+
#define LLVM_LIBC_SRC_MATH_SINHF_H
11+
12+
namespace __llvm_libc {
13+
14+
float sinhf(float x);
15+
16+
} // namespace __llvm_libc
17+
18+
#endif // LLVM_LIBC_SRC_MATH_SINHF_H

libc/test/src/math/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,6 +1330,22 @@ add_fp_unittest(
13301330
libc.src.__support.FPUtil.fputil
13311331
)
13321332

1333+
add_fp_unittest(
1334+
sinhf_test
1335+
NEED_MPFR
1336+
SUITE
1337+
libc_math_unittests
1338+
SRCS
1339+
sinhf_test.cpp
1340+
HDRS
1341+
sdcomp26094.h
1342+
DEPENDS
1343+
libc.include.errno
1344+
libc.src.math.sinhf
1345+
libc.src.__support.CPP.array
1346+
libc.src.__support.FPUtil.fputil
1347+
)
1348+
13331349
add_subdirectory(generic)
13341350
add_subdirectory(exhaustive)
13351351
add_subdirectory(differential_testing)

libc/test/src/math/exhaustive/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,20 @@ add_fp_unittest(
219219
-lpthread
220220
)
221221

222+
add_fp_unittest(
223+
sinhf_test
224+
NO_RUN_POSTBUILD
225+
NEED_MPFR
226+
SUITE
227+
libc_math_exhaustive_tests
228+
SRCS
229+
sinhf_test.cpp
230+
DEPENDS
231+
.exhaustive_test
232+
libc.include.math
233+
libc.src.math.sinhf
234+
libc.src.__support.FPUtil.fputil
235+
LINK_LIBRARIES
236+
-lpthread
237+
)
238+
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===-- Exhaustive test for sinhf -----------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "exhaustive_test.h"
10+
#include "src/__support/FPUtil/FPBits.h"
11+
#include "src/math/sinhf.h"
12+
#include "utils/MPFRWrapper/MPFRUtils.h"
13+
#include "utils/UnitTest/FPMatcher.h"
14+
15+
#include <thread>
16+
17+
using FPBits = __llvm_libc::fputil::FPBits<float>;
18+
19+
namespace mpfr = __llvm_libc::testing::mpfr;
20+
21+
struct LlvmLibcSinhfExhaustiveTest : public LlvmLibcExhaustiveTest<uint32_t> {
22+
bool check(uint32_t start, uint32_t stop,
23+
mpfr::RoundingMode rounding) override {
24+
mpfr::ForceRoundingMode r(rounding);
25+
uint32_t bits = start;
26+
bool result = true;
27+
do {
28+
FPBits xbits(bits);
29+
float x = float(xbits);
30+
result &= EXPECT_MPFR_MATCH(mpfr::Operation::Sinh, x,
31+
__llvm_libc::sinhf(x), 0.5, rounding);
32+
} while (bits++ < stop);
33+
return result;
34+
}
35+
};
36+
37+
// Range: [0, 90];
38+
static constexpr uint32_t POS_START = 0x0000'0000U;
39+
static constexpr uint32_t POS_STOP = 0x42b4'0000U;
40+
41+
TEST_F(LlvmLibcSinhfExhaustiveTest, PostiveRangeRoundNearestTieToEven) {
42+
test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::Nearest);
43+
}
44+
45+
TEST_F(LlvmLibcSinhfExhaustiveTest, PostiveRangeRoundUp) {
46+
test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::Upward);
47+
}
48+
49+
TEST_F(LlvmLibcSinhfExhaustiveTest, PostiveRangeRoundDown) {
50+
test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::Downward);
51+
}
52+
53+
TEST_F(LlvmLibcSinhfExhaustiveTest, PostiveRangeRoundTowardZero) {
54+
test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::TowardZero);
55+
}
56+
57+
// Range: [-90, 0];
58+
static constexpr uint32_t NEG_START = 0x8000'0000U;
59+
static constexpr uint32_t NEG_STOP = 0xc2b4'0000U;
60+
61+
TEST_F(LlvmLibcSinhfExhaustiveTest, NegativeRangeRoundNearestTieToEven) {
62+
test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::Nearest);
63+
}
64+
65+
TEST_F(LlvmLibcSinhfExhaustiveTest, NegativeRangeRoundUp) {
66+
test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::Upward);
67+
}
68+
69+
TEST_F(LlvmLibcSinhfExhaustiveTest, NegativeRangeRoundDown) {
70+
test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::Downward);
71+
}
72+
73+
TEST_F(LlvmLibcSinhfExhaustiveTest, NegativeRangeRoundTowardZero) {
74+
test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::TowardZero);
75+
}

libc/test/src/math/sinhf_test.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//===-- Unittests for sinhf -----------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/__support/CPP/Array.h"
10+
#include "src/__support/FPUtil/FPBits.h"
11+
#include "src/math/sinhf.h"
12+
#include "utils/MPFRWrapper/MPFRUtils.h"
13+
#include "utils/UnitTest/FPMatcher.h"
14+
#include "utils/UnitTest/Test.h"
15+
#include <math.h>
16+
17+
#include <errno.h>
18+
#include <stdint.h>
19+
20+
using FPBits = __llvm_libc::fputil::FPBits<float>;
21+
22+
namespace mpfr = __llvm_libc::testing::mpfr;
23+
24+
DECLARE_SPECIAL_CONSTANTS(float)
25+
26+
TEST(LlvmLibcSinhfTest, SpecialNumbers) {
27+
errno = 0;
28+
29+
EXPECT_FP_EQ(aNaN, __llvm_libc::sinhf(aNaN));
30+
EXPECT_MATH_ERRNO(0);
31+
32+
EXPECT_FP_EQ(0.0f, __llvm_libc::sinhf(0.0f));
33+
EXPECT_MATH_ERRNO(0);
34+
35+
EXPECT_FP_EQ(-0.0f, __llvm_libc::sinhf(-0.0f));
36+
EXPECT_MATH_ERRNO(0);
37+
38+
EXPECT_FP_EQ(inf, __llvm_libc::sinhf(inf));
39+
EXPECT_MATH_ERRNO(0);
40+
41+
EXPECT_FP_EQ(neg_inf, __llvm_libc::sinhf(neg_inf));
42+
EXPECT_MATH_ERRNO(0);
43+
}
44+
45+
TEST(LlvmLibcSinhfTest, InFloatRange) {
46+
constexpr uint32_t COUNT = 1000000;
47+
constexpr uint32_t STEP = UINT32_MAX / COUNT;
48+
for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
49+
float x = float(FPBits(v));
50+
if (isnan(x) || isinf(x))
51+
continue;
52+
ASSERT_MPFR_MATCH(mpfr::Operation::Sinh, x, __llvm_libc::sinhf(x), 0.5);
53+
}
54+
}
55+
56+
// For small values, sinh(x) is x.
57+
TEST(LlvmLibcSinhfTest, SmallValues) {
58+
float x = float(FPBits(uint32_t(0x17800000)));
59+
float result = __llvm_libc::sinhf(x);
60+
EXPECT_MPFR_MATCH(mpfr::Operation::Sinh, x, result, 0.5);
61+
EXPECT_FP_EQ(x, result);
62+
63+
x = float(FPBits(uint32_t(0x00400000)));
64+
result = __llvm_libc::sinhf(x);
65+
EXPECT_MPFR_MATCH(mpfr::Operation::Sinh, x, result, 0.5);
66+
EXPECT_FP_EQ(x, result);
67+
}
68+
69+
TEST(LlvmLibcSinhfTest, Overflow) {
70+
errno = 0;
71+
EXPECT_FP_EQ(inf, __llvm_libc::sinhf(float(FPBits(0x7f7fffffU))));
72+
EXPECT_MATH_ERRNO(ERANGE);
73+
74+
EXPECT_FP_EQ(inf, __llvm_libc::sinhf(float(FPBits(0x42cffff8U))));
75+
EXPECT_MATH_ERRNO(ERANGE);
76+
77+
EXPECT_FP_EQ(inf, __llvm_libc::sinhf(float(FPBits(0x42d00008U))));
78+
EXPECT_MATH_ERRNO(ERANGE);
79+
}
80+
81+
TEST(LlvmLibcSinhfTest, ExceptionalValues) {
82+
float x = float(FPBits(uint32_t(0x3a12'85ffU)));
83+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinh, x,
84+
__llvm_libc::sinhf(x), 0.5);
85+
86+
x = -float(FPBits(uint32_t(0x3a12'85ffU)));
87+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinh, x,
88+
__llvm_libc::sinhf(x), 0.5);
89+
}

0 commit comments

Comments
 (0)