Skip to content

Commit f8834ed

Browse files
[libc][C23][math] Implement cospif function correctly rounded for all rounding modes (#97464)
I also fixed a comment in sinpif.cpp in the first commit. Should this be included in this PR? All tests were passed, including the exhaustive test. CC: @lntue
1 parent de88b2c commit f8834ed

File tree

18 files changed

+406
-4
lines changed

18 files changed

+406
-4
lines changed

libc/config/darwin/arm/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ set(TARGET_LIBM_ENTRYPOINTS
132132
libc.src.math.coshf
133133
libc.src.math.cos
134134
libc.src.math.cosf
135+
libc.src.math.cospif
135136
libc.src.math.erff
136137
libc.src.math.exp
137138
libc.src.math.expf

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ set(TARGET_LIBM_ENTRYPOINTS
346346
libc.src.math.cos
347347
libc.src.math.cosf
348348
libc.src.math.coshf
349+
libc.src.math.cospif
349350
libc.src.math.erff
350351
libc.src.math.exp
351352
libc.src.math.exp10

libc/config/linux/riscv/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ set(TARGET_LIBM_ENTRYPOINTS
354354
libc.src.math.cos
355355
libc.src.math.cosf
356356
libc.src.math.coshf
357+
libc.src.math.cospif
357358
libc.src.math.erff
358359
libc.src.math.exp
359360
libc.src.math.exp10

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ set(TARGET_LIBM_ENTRYPOINTS
371371
libc.src.math.cos
372372
libc.src.math.cosf
373373
libc.src.math.coshf
374+
libc.src.math.cospif
374375
libc.src.math.erff
375376
libc.src.math.exp
376377
libc.src.math.exp10

libc/docs/math/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ Higher Math Functions
274274
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
275275
| cosh | |check| | | | | | 7.12.5.4 | F.10.2.4 |
276276
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
277-
| cospi | | | | | | 7.12.4.12 | F.10.1.12 |
277+
| cospi | |check| | | | | | 7.12.4.12 | F.10.1.12 |
278278
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
279279
| dsqrt | N/A | N/A | | N/A | | 7.12.14.6 | F.10.11 |
280280
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+

libc/src/math/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ add_math_entrypoint_object(cos)
8181
add_math_entrypoint_object(cosf)
8282
add_math_entrypoint_object(cosh)
8383
add_math_entrypoint_object(coshf)
84+
add_math_entrypoint_object(cospif)
8485

8586
add_math_entrypoint_object(erf)
8687
add_math_entrypoint_object(erff)

libc/src/math/cospif.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//===-- Implementation header for cospif ------------------------*- 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_COSPIF_H
10+
#define LLVM_LIBC_SRC_MATH_COSPIF_H
11+
12+
namespace LIBC_NAMESPACE {
13+
14+
float cospif(float x);
15+
16+
} // namespace LIBC_NAMESPACE
17+
18+
#endif // LLVM_LIBC_SRC_MATH_COSPIF_H

libc/src/math/generic/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,23 @@ add_entrypoint_object(
217217
-O3
218218
)
219219

220+
add_entrypoint_object(
221+
cospif
222+
SRCS
223+
cospif.cpp
224+
HDRS
225+
../cospif.h
226+
DEPENDS
227+
.sincosf_utils
228+
libc.src.__support.FPUtil.fenv_impl
229+
libc.src.__support.FPUtil.fp_bits
230+
libc.src.__support.FPUtil.fma
231+
libc.src.__support.FPUtil.multiply_add
232+
libc.src.__support.macros.optimization
233+
COMPILE_OPTIONS
234+
-O3
235+
)
236+
220237
add_entrypoint_object(
221238
sin
222239
SRCS

libc/src/math/generic/cospif.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//===-- Single-precision cospi 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/cospif.h"
10+
#include "sincosf_utils.h"
11+
#include "src/__support/FPUtil/FEnvImpl.h"
12+
#include "src/__support/FPUtil/FPBits.h"
13+
#include "src/__support/FPUtil/multiply_add.h"
14+
#include "src/__support/common.h"
15+
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
16+
#include "src/__support/macros/properties/cpu_features.h" // LIBC_TARGET_CPU_HAS_FMA
17+
18+
namespace LIBC_NAMESPACE {
19+
20+
LLVM_LIBC_FUNCTION(float, cospif, (float x)) {
21+
using FPBits = typename fputil::FPBits<float>;
22+
23+
FPBits xbits(x);
24+
Sign xsign = xbits.sign();
25+
xbits.set_sign(Sign::POS);
26+
27+
uint32_t x_abs = xbits.uintval();
28+
double xd = static_cast<double>(xbits.get_val());
29+
30+
// Range reduction:
31+
// For |x| > 1/32, we perform range reduction as follows:
32+
// Find k and y such that:
33+
// x = (k + y) * 1/32
34+
// k is an integer
35+
// |y| < 0.5
36+
//
37+
// This is done by performing:
38+
// k = round(x * 32)
39+
// y = x * 32 - k
40+
//
41+
// Once k and y are computed, we then deduce the answer by the cosine of sum
42+
// formula:
43+
// cospi(x) = cos((k + y)*pi/32)
44+
// = cos(y*pi/32) * cos(k*pi/32) - sin(y*pi/32) * sin(k*pi/32)
45+
// The values of sin(k*pi/32) and cos(k*pi/32) for k = 0..63 are precomputed
46+
// and stored using a vector of 32 doubles. Sin(y*pi/32) and cos(y*pi/32) are
47+
// computed using degree-7 and degree-6 minimax polynomials generated by
48+
// Sollya respectively.
49+
50+
// The exhautive test passes for smaller values
51+
if (LIBC_UNLIKELY(x_abs < 0x38A2'F984U)) {
52+
53+
#if defined(LIBC_TARGET_CPU_HAS_FMA)
54+
return fputil::multiply_add(xbits.get_val(), -0x1.0p-25f, 1.0f);
55+
#else
56+
return static_cast<float>(fputil::multiply_add(xd, -0x1.0p-25, 1.0));
57+
#endif // LIBC_TARGET_CPU_HAS_FMA
58+
}
59+
60+
// Numbers greater or equal to 2^23 are always integers or NaN
61+
if (LIBC_UNLIKELY(x_abs >= 0x4B00'0000)) {
62+
63+
if (LIBC_UNLIKELY(x_abs < 0x4B80'0000)) {
64+
return (x_abs & 0x1) ? -1.0f : 1.0f;
65+
}
66+
67+
// x is inf or nan.
68+
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
69+
if (x_abs == 0x7f80'0000U) {
70+
fputil::set_errno_if_required(EDOM);
71+
fputil::raise_except_if_required(FE_INVALID);
72+
}
73+
return x + FPBits::quiet_nan().get_val();
74+
}
75+
76+
return 1.0f;
77+
}
78+
79+
// Combine the results with the sine of sum formula:
80+
// cos(pi * x) = cos((k + y)*pi/32)
81+
// = cos(y*pi/32) * cos(k*pi/32) - sin(y*pi/32) * sin(k*pi/32)
82+
// = (cosm1_y + 1) * cos_k - sin_y * sin_k
83+
// = (cosm1_y * cos_k + cos_k) - sin_y * sin_k
84+
double sin_k, cos_k, sin_y, cosm1_y;
85+
86+
sincospif_eval(xd, sin_k, cos_k, sin_y, cosm1_y);
87+
88+
if (LIBC_UNLIKELY(sin_y == 0 && cos_k == 0)) {
89+
return FPBits::zero(xsign).get_val();
90+
}
91+
92+
return static_cast<float>(fputil::multiply_add(
93+
sin_y, -sin_k, fputil::multiply_add(cosm1_y, cos_k, cos_k)));
94+
}
95+
96+
} // namespace LIBC_NAMESPACE

libc/src/math/generic/sinpif.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
2626
double xd = static_cast<double>(x);
2727

2828
// Range reduction:
29-
// For |x| > pi/32, we perform range reduction as follows:
29+
// For |x| > 1/32, we perform range reduction as follows:
3030
// Find k and y such that:
3131
// x = (k + y) * 1/32
3232
// k is an integer
3333
// |y| < 0.5
34-
// For small range (|x| < 2^45 when FMA instructions are available, 2^22
35-
// otherwise), this is done by performing:
34+
//
35+
// This is done by performing:
3636
// k = round(x * 32)
3737
// y = x * 32 - k
3838
//

libc/test/src/math/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,22 @@ add_fp_unittest(
2828
libc.src.__support.FPUtil.fp_bits
2929
)
3030

31+
add_fp_unittest(
32+
cospif_test
33+
NEED_MPFR
34+
SUITE
35+
libc-math-unittests
36+
SRCS
37+
cospif_test.cpp
38+
HDRS
39+
sdcomp26094.h
40+
DEPENDS
41+
libc.src.errno.errno
42+
libc.src.math.cospif
43+
libc.src.__support.CPP.array
44+
libc.src.__support.FPUtil.fp_bits
45+
)
46+
3147
add_fp_unittest(
3248
sinf_test
3349
NEED_MPFR

libc/test/src/math/cospif_test.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//===-- Unittests for cospif ----------------------------------------------===//
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/errno/libc_errno.h"
10+
#include "src/math/cospif.h"
11+
#include "test/UnitTest/FPMatcher.h"
12+
#include "test/src/math/sdcomp26094.h"
13+
#include "utils/MPFRWrapper/MPFRUtils.h"
14+
15+
using LlvmLibcCospifTest = LIBC_NAMESPACE::testing::FPTest<float>;
16+
17+
using LIBC_NAMESPACE::testing::SDCOMP26094_VALUES;
18+
19+
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
20+
21+
TEST_F(LlvmLibcCospifTest, SpecialNumbers) {
22+
LIBC_NAMESPACE::libc_errno = 0;
23+
24+
EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(aNaN));
25+
EXPECT_MATH_ERRNO(0);
26+
27+
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::cospif(0.0f));
28+
EXPECT_MATH_ERRNO(0);
29+
30+
EXPECT_FP_EQ(1.0f, LIBC_NAMESPACE::cospif(-0.0f));
31+
EXPECT_MATH_ERRNO(0);
32+
33+
EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(inf));
34+
EXPECT_MATH_ERRNO(EDOM);
35+
36+
EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::cospif(neg_inf));
37+
EXPECT_MATH_ERRNO(EDOM);
38+
}
39+
40+
TEST_F(LlvmLibcCospifTest, SpecificBitPatterns) {
41+
constexpr int N = 36;
42+
constexpr uint32_t INPUTS[N] = {
43+
0x3f06'0a92U, // x = pi/6
44+
0x3f3a'dc51U, // x = 0x1.75b8a2p-1f
45+
0x3f49'0fdbU, // x = pi/4
46+
0x3f86'0a92U, // x = pi/3
47+
0x3fa7'832aU, // x = 0x1.4f0654p+0f
48+
0x3fc9'0fdbU, // x = pi/2
49+
0x4017'1973U, // x = 0x1.2e32e6p+1f
50+
0x4049'0fdbU, // x = pi
51+
0x4096'cbe4U, // x = 0x1.2d97c8p+2f
52+
0x40c9'0fdbU, // x = 2*pi
53+
0x433b'7490U, // x = 0x1.76e92p+7f
54+
0x437c'e5f1U, // x = 0x1.f9cbe2p+7f
55+
0x4619'9998U, // x = 0x1.33333p+13f
56+
0x474d'246fU, // x = 0x1.9a48dep+15f
57+
0x4afd'ece4U, // x = 0x1.fbd9c8p+22f
58+
0x4c23'32e9U, // x = 0x1.4665d2p+25f
59+
0x50a3'e87fU, // x = 0x1.47d0fep+34f
60+
0x5239'47f6U, // x = 0x1.728fecp+37f
61+
0x53b1'46a6U, // x = 0x1.628d4cp+40f
62+
0x55ca'fb2aU, // x = 0x1.95f654p+44f
63+
0x588e'f060U, // x = 0x1.1de0cp+50f
64+
0x5c07'bcd0U, // x = 0x1.0f79ap+57f
65+
0x5ebc'fddeU, // x = 0x1.79fbbcp+62f
66+
0x5fa6'eba7U, // x = 0x1.4dd74ep+64f
67+
0x61a4'0b40U, // x = 0x1.48168p+68f
68+
0x6386'134eU, // x = 0x1.0c269cp+72f
69+
0x6589'8498U, // x = 0x1.13093p+76f
70+
0x6600'0001U, // x = 0x1.000002p+77f
71+
0x664e'46e4U, // x = 0x1.9c8dc8p+77f
72+
0x66b0'14aaU, // x = 0x1.602954p+78f
73+
0x67a9'242bU, // x = 0x1.524856p+80f
74+
0x6a19'76f1U, // x = 0x1.32ede2p+85f
75+
0x6c55'da58U, // x = 0x1.abb4bp+89f
76+
0x6f79'be45U, // x = 0x1.f37c8ap+95f
77+
0x7276'69d4U, // x = 0x1.ecd3a8p+101f
78+
0x7758'4625U, // x = 0x1.b08c4ap+111f
79+
};
80+
81+
for (int i = 0; i < N; ++i) {
82+
float x = FPBits(INPUTS[i]).get_val();
83+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, x,
84+
LIBC_NAMESPACE::cospif(x), 0.5);
85+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, -x,
86+
LIBC_NAMESPACE::cospif(-x), 0.5);
87+
}
88+
}
89+
90+
// For small values, sinpi(x) is pi * x.
91+
TEST_F(LlvmLibcCospifTest, SmallValues) {
92+
float x = FPBits(0x1780'0000U).get_val();
93+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, x,
94+
LIBC_NAMESPACE::cospif(x), 0.5);
95+
96+
x = FPBits(0x0040'0000U).get_val();
97+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, x,
98+
LIBC_NAMESPACE::cospif(x), 0.5);
99+
}
100+
101+
// SDCOMP-26094: check sinfpi in the cases for which the range reducer
102+
// returns values furthest beyond its nominal upper bound of pi/4.
103+
TEST_F(LlvmLibcCospifTest, SDCOMP_26094) {
104+
for (uint32_t v : SDCOMP26094_VALUES) {
105+
float x = FPBits((v)).get_val();
106+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Cospi, x,
107+
LIBC_NAMESPACE::cospif(x), 0.5);
108+
}
109+
}
110+
111+
// sinpi(-n) = -0.0
112+
// sinpi(+n) = +0.0
113+
TEST_F(LlvmLibcCospifTest, SignedZeros) {
114+
EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::cospif(100.5f));
115+
EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::cospif(-100.5f));
116+
EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::cospif(45678.5f));
117+
EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::cospif(-45678.5f));
118+
EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::cospif(8000000.5f));
119+
EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::cospif(-8000000.5f));
120+
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,22 @@ add_fp_unittest(
7474
-lpthread
7575
)
7676

77+
add_fp_unittest(
78+
cospif_test
79+
NO_RUN_POSTBUILD
80+
NEED_MPFR
81+
SUITE
82+
libc_math_exhaustive_tests
83+
SRCS
84+
cospif_test.cpp
85+
DEPENDS
86+
.exhaustive_test
87+
libc.src.math.cospif
88+
libc.src.__support.FPUtil.fp_bits
89+
LINK_LIBRARIES
90+
-lpthread
91+
)
92+
7793
add_fp_unittest(
7894
sincosf_test
7995
NO_RUN_POSTBUILD

0 commit comments

Comments
 (0)