Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5fedf7f

Browse files
author
Siva Chandra Reddy
committedApr 16, 2020
[libc] Move implementations of cosf, sinf, sincosf to src/math directory.
NFC intended in the implementaton. Only mechanical changes to fit the LLVM libc implementation standard have been done. Math testing infrastructure has been added. This infrastructure compares the results produced by the libc with the high precision results from MPFR. Tests making use of this infrastructure have been added for cosf, sinf and sincosf. Reviewers: abrachet, phosek Differential Revision: https://reviews.llvm.org/D76825
1 parent 44c4ba3 commit 5fedf7f

34 files changed

+1238
-541
lines changed
 

‎libc/AOR_v20.02/math/cosf.c

Lines changed: 0 additions & 64 deletions
This file was deleted.

‎libc/AOR_v20.02/math/sincosf.c

Lines changed: 0 additions & 80 deletions
This file was deleted.

‎libc/AOR_v20.02/math/sincosf.h

Lines changed: 0 additions & 154 deletions
This file was deleted.

‎libc/AOR_v20.02/math/sincosf_data.c

Lines changed: 0 additions & 64 deletions
This file was deleted.

‎libc/AOR_v20.02/math/sinf.c

Lines changed: 0 additions & 68 deletions
This file was deleted.

‎libc/AOR_v20.02/math/test/testcases/directed/cosf.tst

Lines changed: 0 additions & 26 deletions
This file was deleted.

‎libc/AOR_v20.02/math/test/testcases/directed/sincosf.tst

Lines changed: 0 additions & 52 deletions
This file was deleted.

‎libc/AOR_v20.02/math/test/testcases/directed/sinf.tst

Lines changed: 0 additions & 29 deletions
This file was deleted.

‎libc/AOR_v20.02/math/test/testcases/random/float.tst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44
!! See https://llvm.org/LICENSE.txt for license information.
55
!! SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66

7-
test sinf 10000
8-
test cosf 10000
9-
test sincosf_sinf 5000
10-
test sincosf_cosf 5000
117
test tanf 10000
128
test expf 10000
139
test exp2f 10000

‎libc/config/linux/api.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
include "config/public_api.td"
22

3+
include "spec/gnu_ext.td"
34
include "spec/linux.td"
45
include "spec/posix.td"
56
include "spec/stdc.td"
@@ -123,7 +124,10 @@ def MathAPI : PublicAPI<"math.h"> {
123124
IsNanMacro,
124125
];
125126
let Functions = [
127+
"cosf",
126128
"round",
129+
"sincosf",
130+
"sinf",
127131
];
128132
}
129133

‎libc/lib/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ add_entrypoint_library(
4141
llvmlibm
4242
DEPENDS
4343
# math.h entrypoints
44+
libc.src.math.cosf
4445
libc.src.math.round
46+
libc.src.math.sincosf
47+
libc.src.math.sinf
4548
)
4649

4750
add_redirector_library(

‎libc/src/__support/common.h.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
#define LIBC_INLINE_ASM __asm__ __volatile__
1313

14+
#define likely(x) __builtin_expect (!!(x), 1)
15+
#define unlikely(x) __builtin_expect (x, 0)
16+
#define UNUSED __attribute__((unused))
17+
1418
<!> Include the platform specific definitions at build time. For example, that
1519
<!> of entrypoint macro.
1620
%%include_file(${platform_defs})

‎libc/src/math/CMakeLists.txt

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
add_header_library(
2+
math_utils
3+
HDRS
4+
math_utils.h
5+
DEPENDS
6+
libc.include.errno
7+
libc.include.math
8+
libc.src.errno.__errno_location
9+
)
10+
11+
add_object_library(
12+
sincosf_utils
13+
HDRS
14+
sincosf_utils.h
15+
SRCS
16+
sincosf_data.cpp
17+
DEPENDS
18+
.math_utils
19+
)
20+
121
add_entrypoint_object(
222
round
323
REDIRECTED
@@ -12,3 +32,39 @@ add_redirector_object(
1232
SRC
1333
round_redirector.cpp
1434
)
35+
36+
add_entrypoint_object(
37+
cosf
38+
SRCS
39+
cosf.cpp
40+
HDRS
41+
cosf.h
42+
DEPENDS
43+
.sincosf_utils
44+
libc.include.math
45+
libc.src.errno.__errno_location
46+
)
47+
48+
add_entrypoint_object(
49+
sinf
50+
SRCS
51+
sinf.cpp
52+
HDRS
53+
sinf.h
54+
DEPENDS
55+
.sincosf_utils
56+
libc.include.math
57+
libc.src.errno.__errno_location
58+
)
59+
60+
add_entrypoint_object(
61+
sincosf
62+
SRCS
63+
sincosf.cpp
64+
HDRS
65+
sincosf.h
66+
DEPENDS
67+
.sincosf_utils
68+
libc.include.math
69+
libc.src.errno.__errno_location
70+
)

‎libc/src/math/cosf.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//===-- Single-precision cos 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 "math_utils.h"
10+
#include "sincosf_utils.h"
11+
12+
#include "include/math.h"
13+
#include "src/__support/common.h"
14+
15+
#include <stdint.h>
16+
17+
namespace __llvm_libc {
18+
19+
// Fast cosf implementation. Worst-case ULP is 0.5607, maximum relative
20+
// error is 0.5303 * 2^-23. A single-step range reduction is used for
21+
// small values. Large inputs have their range reduced using fast integer
22+
// arithmetic.
23+
float LLVM_LIBC_ENTRYPOINT(cosf)(float y) {
24+
double x = y;
25+
double s;
26+
int n;
27+
const sincos_t *p = &__sincosf_table[0];
28+
29+
if (abstop12(y) < abstop12(pio4)) {
30+
double x2 = x * x;
31+
32+
if (unlikely(abstop12(y) < abstop12(as_float(0x39800000))))
33+
return 1.0f;
34+
35+
return sinf_poly(x, x2, p, 1);
36+
} else if (likely(abstop12(y) < abstop12(120.0f))) {
37+
x = reduce_fast(x, p, &n);
38+
39+
// Setup the signs for sin and cos.
40+
s = p->sign[n & 3];
41+
42+
if (n & 2)
43+
p = &__sincosf_table[1];
44+
45+
return sinf_poly(x * s, x * x, p, n ^ 1);
46+
} else if (abstop12(y) < abstop12(INFINITY)) {
47+
uint32_t xi = as_uint32_bits(y);
48+
int sign = xi >> 31;
49+
50+
x = reduce_large(xi, &n);
51+
52+
// Setup signs for sin and cos - include original sign.
53+
s = p->sign[(n + sign) & 3];
54+
55+
if ((n + sign) & 2)
56+
p = &__sincosf_table[1];
57+
58+
return sinf_poly(x * s, x * x, p, n ^ 1);
59+
}
60+
61+
return invalidf(y);
62+
}
63+
64+
} // namespace __llvm_libc

‎libc/src/math/cosf.h

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

‎libc/src/math/math_utils.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===-- Collection of utils for implementing math functions -----*- 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_MATH_UTILS_H
10+
#define LLVM_LIBC_SRC_MATH_MATH_UTILS_H
11+
12+
#include "include/errno.h"
13+
#include "include/math.h"
14+
15+
#include "src/__support/common.h"
16+
#include "src/errno/llvmlibc_errno.h"
17+
18+
#include <stdint.h>
19+
20+
namespace __llvm_libc {
21+
22+
static inline float with_errnof(float x, int err) {
23+
if (math_errhandling & MATH_ERRNO)
24+
llvmlibc_errno = err;
25+
return x;
26+
}
27+
28+
static inline uint32_t as_uint32_bits(float x) {
29+
return *reinterpret_cast<uint32_t *>(&x);
30+
}
31+
32+
static inline float as_float(uint32_t x) {
33+
return *reinterpret_cast<float *>(&x);
34+
}
35+
36+
static inline double as_double(uint64_t x) {
37+
return *reinterpret_cast<double *>(&x);
38+
}
39+
40+
static inline constexpr float invalidf(float x) {
41+
float y = (x - x) / (x - x);
42+
return isnan(x) ? y : with_errnof(y, EDOM);
43+
}
44+
45+
static inline void force_eval_float(float x) { volatile float y UNUSED = x; }
46+
47+
} // namespace __llvm_libc
48+
49+
#endif // LLVM_LIBC_SRC_MATH_MATH_UTILS_H

‎libc/src/math/sincosf.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//===-- Single-precision sincos 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 "math_utils.h"
10+
#include "sincosf_utils.h"
11+
12+
#include "include/math.h"
13+
#include "src/__support/common.h"
14+
15+
#include <stdint.h>
16+
17+
namespace __llvm_libc {
18+
19+
// Fast sincosf implementation. Worst-case ULP is 0.5607, maximum relative
20+
// error is 0.5303 * 2^-23. A single-step range reduction is used for
21+
// small values. Large inputs have their range reduced using fast integer
22+
// arithmetic.
23+
void LLVM_LIBC_ENTRYPOINT(sincosf)(float y, float *sinp, float *cosp) {
24+
double x = y;
25+
double s;
26+
int n;
27+
const sincos_t *p = &__sincosf_table[0];
28+
29+
if (abstop12(y) < abstop12(pio4)) {
30+
double x2 = x * x;
31+
32+
if (unlikely(abstop12(y) < abstop12(as_float(0x39800000)))) {
33+
if (unlikely(abstop12(y) < abstop12(as_float(0x800000))))
34+
// Force underflow for tiny y.
35+
force_eval_float(x2);
36+
*sinp = y;
37+
*cosp = 1.0f;
38+
return;
39+
}
40+
41+
sincosf_poly(x, x2, p, 0, sinp, cosp);
42+
} else if (abstop12(y) < abstop12(120.0f)) {
43+
x = reduce_fast(x, p, &n);
44+
45+
// Setup the signs for sin and cos.
46+
s = p->sign[n & 3];
47+
48+
if (n & 2)
49+
p = &__sincosf_table[1];
50+
51+
sincosf_poly(x * s, x * x, p, n, sinp, cosp);
52+
} else if (likely(abstop12(y) < abstop12(INFINITY))) {
53+
uint32_t xi = as_uint32_bits(y);
54+
int sign = xi >> 31;
55+
56+
x = reduce_large(xi, &n);
57+
58+
// Setup signs for sin and cos - include original sign.
59+
s = p->sign[(n + sign) & 3];
60+
61+
if ((n + sign) & 2)
62+
p = &__sincosf_table[1];
63+
64+
sincosf_poly(x * s, x * x, p, n, sinp, cosp);
65+
} else {
66+
// Return NaN if Inf or NaN for both sin and cos.
67+
*sinp = *cosp = y - y;
68+
69+
// Needed to set errno for +-Inf, the add is a hack to work
70+
// around a gcc register allocation issue: just passing y
71+
// affects code generation in the fast path.
72+
invalidf(y + y);
73+
}
74+
}
75+
76+
} // namespace __llvm_libc

‎libc/src/math/sincosf.h

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

‎libc/src/math/sincosf_data.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===-- sinf/cosf data tables ---------------------------------------------===//
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 "math_utils.h"
10+
#include "sincosf_utils.h"
11+
12+
#include <stdint.h>
13+
14+
namespace __llvm_libc {
15+
16+
// The constants and polynomials for sine and cosine. The 2nd entry
17+
// computes -cos (x) rather than cos (x) to get negation for free.
18+
const sincos_t __sincosf_table[2] = {
19+
{{1.0, -1.0, -1.0, 1.0},
20+
as_double(0x41645f306dc9c883),
21+
as_double(0x3ff921fb54442d18),
22+
as_double(0x3ff0000000000000),
23+
as_double(0xbfdffffffd0c621c),
24+
as_double(0x3fa55553e1068f19),
25+
as_double(0xbf56c087e89a359d),
26+
as_double(0x3ef99343027bf8c3),
27+
as_double(0xbfc555545995a603),
28+
as_double(0x3f81107605230bc4),
29+
as_double(0xbf2994eb3774cf24)},
30+
{{1.0, -1.0, -1.0, 1.0},
31+
as_double(0x41645f306dc9c883),
32+
as_double(0x3ff921fb54442d18),
33+
as_double(0xbff0000000000000),
34+
as_double(0x3fdffffffd0c621c),
35+
as_double(0xbfa55553e1068f19),
36+
as_double(0x3f56c087e89a359d),
37+
as_double(0xbef99343027bf8c3),
38+
as_double(0xbfc555545995a603),
39+
as_double(0x3f81107605230bc4),
40+
as_double(0xbf2994eb3774cf24)},
41+
};
42+
43+
// Table with 4/PI to 192 bit precision. To avoid unaligned accesses
44+
// only 8 new bits are added per entry, making the table 4 times larger.
45+
const uint32_t __inv_pio4[24] = {
46+
0xa2, 0xa2f9, 0xa2f983, 0xa2f9836e, 0xf9836e4e, 0x836e4e44,
47+
0x6e4e4415, 0x4e441529, 0x441529fc, 0x1529fc27, 0x29fc2757, 0xfc2757d1,
48+
0x2757d1f5, 0x57d1f534, 0xd1f534dd, 0xf534ddc0, 0x34ddc0db, 0xddc0db62,
49+
0xc0db6295, 0xdb629599, 0x6295993c, 0x95993c43, 0x993c4390, 0x3c439041};
50+
51+
} // namespace __llvm_libc

‎libc/src/math/sincosf_utils.h

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
//===-- Collection of utils for cosf/sinf/sincosf ---------------*- 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_SINCOSF_UTILS_H
10+
#define LLVM_LIBC_SRC_MATH_SINCOSF_UTILS_H
11+
12+
#include "math_utils.h"
13+
14+
#include <stdint.h>
15+
16+
namespace __llvm_libc {
17+
18+
// 2PI * 2^-64.
19+
static const double pi63 = as_double(0x3c1921fb54442d18);
20+
// PI / 4.
21+
static const double pio4 = as_double(0x3fe921fb54442d18);
22+
23+
// The constants and polynomials for sine and cosine.
24+
typedef struct {
25+
double sign[4]; // Sign of sine in quadrants 0..3.
26+
double hpi_inv; // 2 / PI ( * 2^24 ).
27+
double hpi; // PI / 2.
28+
double c0, c1, c2, c3, c4; // Cosine polynomial.
29+
double s1, s2, s3; // Sine polynomial.
30+
} sincos_t;
31+
32+
// Polynomial data (the cosine polynomial is negated in the 2nd entry).
33+
extern const sincos_t __sincosf_table[2];
34+
35+
// Table with 4/PI to 192 bit precision.
36+
extern const uint32_t __inv_pio4[];
37+
38+
// Top 12 bits of the float representation with the sign bit cleared.
39+
static inline uint32_t abstop12(float x) {
40+
return (as_uint32_bits(x) >> 20) & 0x7ff;
41+
}
42+
43+
// Compute the sine and cosine of inputs X and X2 (X squared), using the
44+
// polynomial P and store the results in SINP and COSP. N is the quadrant,
45+
// if odd the cosine and sine polynomials are swapped.
46+
static inline void sincosf_poly(double x, double x2, const sincos_t *p, int n,
47+
float *sinp, float *cosp) {
48+
double x3, x4, x5, x6, s, c, c1, c2, s1;
49+
50+
x4 = x2 * x2;
51+
x3 = x2 * x;
52+
c2 = p->c3 + x2 * p->c4;
53+
s1 = p->s2 + x2 * p->s3;
54+
55+
// Swap sin/cos result based on quadrant.
56+
float *tmp = (n & 1 ? cosp : sinp);
57+
cosp = (n & 1 ? sinp : cosp);
58+
sinp = tmp;
59+
60+
c1 = p->c0 + x2 * p->c1;
61+
x5 = x3 * x2;
62+
x6 = x4 * x2;
63+
64+
s = x + x3 * p->s1;
65+
c = c1 + x4 * p->c2;
66+
67+
*sinp = s + x5 * s1;
68+
*cosp = c + x6 * c2;
69+
}
70+
71+
// Return the sine of inputs X and X2 (X squared) using the polynomial P.
72+
// N is the quadrant, and if odd the cosine polynomial is used.
73+
static inline float sinf_poly(double x, double x2, const sincos_t *p, int n) {
74+
double x3, x4, x6, x7, s, c, c1, c2, s1;
75+
76+
if ((n & 1) == 0) {
77+
x3 = x * x2;
78+
s1 = p->s2 + x2 * p->s3;
79+
80+
x7 = x3 * x2;
81+
s = x + x3 * p->s1;
82+
83+
return s + x7 * s1;
84+
} else {
85+
x4 = x2 * x2;
86+
c2 = p->c3 + x2 * p->c4;
87+
c1 = p->c0 + x2 * p->c1;
88+
89+
x6 = x4 * x2;
90+
c = c1 + x4 * p->c2;
91+
92+
return c + x6 * c2;
93+
}
94+
}
95+
96+
// Fast range reduction using single multiply-subtract. Return the modulo of
97+
// X as a value between -PI/4 and PI/4 and store the quadrant in NP.
98+
// The values for PI/2 and 2/PI are accessed via P. Since PI/2 as a double
99+
// is accurate to 55 bits and the worst-case cancellation happens at 6 * PI/4,
100+
// the result is accurate for |X| <= 120.0.
101+
static inline double reduce_fast(double x, const sincos_t *p, int *np) {
102+
double r;
103+
// Use scaled float to int conversion with explicit rounding.
104+
// hpi_inv is prescaled by 2^24 so the quadrant ends up in bits 24..31.
105+
// This avoids inaccuracies introduced by truncating negative values.
106+
r = x * p->hpi_inv;
107+
int n = ((int32_t)r + 0x800000) >> 24;
108+
*np = n;
109+
return x - n * p->hpi;
110+
}
111+
112+
// Reduce the range of XI to a multiple of PI/2 using fast integer arithmetic.
113+
// XI is a reinterpreted float and must be >= 2.0f (the sign bit is ignored).
114+
// Return the modulo between -PI/4 and PI/4 and store the quadrant in NP.
115+
// Reduction uses a table of 4/PI with 192 bits of precision. A 32x96->128 bit
116+
// multiply computes the exact 2.62-bit fixed-point modulo. Since the result
117+
// can have at most 29 leading zeros after the binary point, the double
118+
// precision result is accurate to 33 bits.
119+
static inline double reduce_large(uint32_t xi, int *np) {
120+
const uint32_t *arr = &__inv_pio4[(xi >> 26) & 15];
121+
int shift = (xi >> 23) & 7;
122+
uint64_t n, res0, res1, res2;
123+
124+
xi = (xi & 0xffffff) | 0x800000;
125+
xi <<= shift;
126+
127+
res0 = xi * arr[0];
128+
res1 = (uint64_t)xi * arr[4];
129+
res2 = (uint64_t)xi * arr[8];
130+
res0 = (res2 >> 32) | (res0 << 32);
131+
res0 += res1;
132+
133+
n = (res0 + (1ULL << 61)) >> 62;
134+
res0 -= n << 62;
135+
double x = (int64_t)res0;
136+
*np = n;
137+
return x * pi63;
138+
}
139+
140+
} // namespace __llvm_libc
141+
142+
#endif // LLVM_LIBC_SRC_MATH_SINCOSF_UTILS_H

‎libc/src/math/sinf.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//===-- Single-precision sin 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 "math_utils.h"
10+
#include "sincosf_utils.h"
11+
12+
#include "include/math.h"
13+
#include "src/__support/common.h"
14+
15+
#include <stdint.h>
16+
17+
namespace __llvm_libc {
18+
19+
// Fast sinf implementation. Worst-case ULP is 0.5607, maximum relative
20+
// error is 0.5303 * 2^-23. A single-step range reduction is used for
21+
// small values. Large inputs have their range reduced using fast integer
22+
// arithmetic.
23+
float LLVM_LIBC_ENTRYPOINT(sinf)(float y) {
24+
double x = y;
25+
double s;
26+
int n;
27+
const sincos_t *p = &__sincosf_table[0];
28+
29+
if (abstop12(y) < abstop12(pio4)) {
30+
s = x * x;
31+
32+
if (unlikely(abstop12(y) < abstop12(as_float(0x39800000)))) {
33+
if (unlikely(abstop12(y) < abstop12(as_float(0x800000))))
34+
// Force underflow for tiny y.
35+
force_eval_float(s);
36+
return y;
37+
}
38+
39+
return sinf_poly(x, s, p, 0);
40+
} else if (likely(abstop12(y) < abstop12(120.0f))) {
41+
x = reduce_fast(x, p, &n);
42+
43+
// Setup the signs for sin and cos.
44+
s = p->sign[n & 3];
45+
46+
if (n & 2)
47+
p = &__sincosf_table[1];
48+
49+
return sinf_poly(x * s, x * x, p, n);
50+
} else if (abstop12(y) < abstop12(INFINITY)) {
51+
uint32_t xi = as_uint32_bits(y);
52+
int sign = xi >> 31;
53+
54+
x = reduce_large(xi, &n);
55+
56+
// Setup signs for sin and cos - include original sign.
57+
s = p->sign[(n + sign) & 3];
58+
59+
if ((n + sign) & 2)
60+
p = &__sincosf_table[1];
61+
62+
return sinf_poly(x * s, x * x, p, n);
63+
}
64+
65+
return invalidf(y);
66+
}
67+
68+
} // namespace __llvm_libc

‎libc/src/math/sinf.h

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

‎libc/test/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_subdirectory(assert)
22
add_subdirectory(errno)
3+
add_subdirectory(math)
34
add_subdirectory(signal)
45
add_subdirectory(stdio)
56
add_subdirectory(stdlib)

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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
add_libc_testsuite(libc_math_unittests)
2+
3+
function(add_math_unittest name)
4+
cmake_parse_arguments(
5+
"MATH_UNITTEST"
6+
"NEED_MPFR" # No optional arguments
7+
"" # Single value arguments
8+
"" # Multi-value arguments
9+
${ARGN}
10+
)
11+
12+
if(MATH_UNITTEST_NEED_MPFR)
13+
if(NOT LIBC_TESTS_CAN_USE_MPFR)
14+
message("WARNING: Math test ${name} will be skipped as MPFR library is not available.")
15+
return()
16+
endif()
17+
endif()
18+
19+
add_libc_unittest(${name} ${MATH_UNITTEST_UNPARSED_ARGUMENTS})
20+
if(MATH_UNITTEST_NEED_MPFR)
21+
get_fq_target_name(${name} fq_target_name)
22+
target_link_libraries(${fq_target_name} PRIVATE libcMPFRWrapper -lmpfr -lgmp)
23+
endif()
24+
endfunction(add_math_unittest)
25+
26+
add_header_library(
27+
float_utils
28+
HDRS
29+
float.h
30+
)
31+
32+
# TODO(sivachandra): Remove the dependency on __errno_location as the tested
33+
# entry points depend on the already.
34+
add_math_unittest(
35+
cosf_test
36+
NEED_MPFR
37+
SUITE
38+
libc_math_unittests
39+
SRCS
40+
cosf_test.cpp
41+
HDRS
42+
sdcomp26094.h
43+
DEPENDS
44+
.float_utils
45+
libc.src.errno.__errno_location
46+
libc.src.math.cosf
47+
libc.utils.CPP.standalone_cpp
48+
)
49+
50+
add_math_unittest(
51+
sinf_test
52+
NEED_MPFR
53+
SUITE
54+
libc_math_unittests
55+
SRCS
56+
sinf_test.cpp
57+
HDRS
58+
sdcomp26094.h
59+
DEPENDS
60+
.float_utils
61+
libc.src.errno.__errno_location
62+
libc.src.math.sinf
63+
libc.utils.CPP.standalone_cpp
64+
)
65+
66+
add_math_unittest(
67+
sincosf_test
68+
NEED_MPFR
69+
SUITE
70+
libc_math_unittests
71+
SRCS
72+
sincosf_test.cpp
73+
HDRS
74+
sdcomp26094.h
75+
DEPENDS
76+
.float_utils
77+
libc.src.errno.__errno_location
78+
libc.src.math.sincosf
79+
libc.utils.CPP.standalone_cpp
80+
)

‎libc/test/src/math/cosf_test.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//===-- Unittests for cosf ------------------------------------------------===//
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 "include/math.h"
10+
#include "src/errno/llvmlibc_errno.h"
11+
#include "src/math/cosf.h"
12+
#include "src/math/math_utils.h"
13+
#include "test/src/math/float.h"
14+
#include "test/src/math/sdcomp26094.h"
15+
#include "utils/CPP/Array.h"
16+
#include "utils/MPFRWrapper/MPFRUtils.h"
17+
#include "utils/UnitTest/Test.h"
18+
19+
#include <stdint.h>
20+
21+
using __llvm_libc::as_float;
22+
using __llvm_libc::as_uint32_bits;
23+
24+
using __llvm_libc::testing::FloatBits;
25+
using __llvm_libc::testing::sdcomp26094Values;
26+
27+
namespace mpfr = __llvm_libc::testing::mpfr;
28+
29+
// 12 additional bits of precision over the base precision of a |float|
30+
// value.
31+
static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12,
32+
3 * 0x1000 / 4};
33+
34+
TEST(CosfTest, SpecialNumbers) {
35+
llvmlibc_errno = 0;
36+
37+
EXPECT_TRUE(FloatBits::isQNan(
38+
as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::QNan)))));
39+
EXPECT_EQ(llvmlibc_errno, 0);
40+
41+
EXPECT_TRUE(FloatBits::isNegQNan(
42+
as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::NegQNan)))));
43+
EXPECT_EQ(llvmlibc_errno, 0);
44+
45+
EXPECT_TRUE(FloatBits::isQNan(
46+
as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::SNan)))));
47+
EXPECT_EQ(llvmlibc_errno, 0);
48+
49+
EXPECT_TRUE(FloatBits::isNegQNan(
50+
as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::NegSNan)))));
51+
EXPECT_EQ(llvmlibc_errno, 0);
52+
53+
EXPECT_EQ(FloatBits::One,
54+
as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::Zero))));
55+
EXPECT_EQ(llvmlibc_errno, 0);
56+
57+
EXPECT_EQ(FloatBits::One,
58+
as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::NegZero))));
59+
EXPECT_EQ(llvmlibc_errno, 0);
60+
61+
llvmlibc_errno = 0;
62+
EXPECT_TRUE(FloatBits::isQNan(
63+
as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::Inf)))));
64+
EXPECT_EQ(llvmlibc_errno, EDOM);
65+
66+
llvmlibc_errno = 0;
67+
EXPECT_TRUE(FloatBits::isNegQNan(
68+
as_uint32_bits(__llvm_libc::cosf(as_float(FloatBits::NegInf)))));
69+
EXPECT_EQ(llvmlibc_errno, EDOM);
70+
}
71+
72+
TEST(CosfTest, InFloatRange) {
73+
constexpr uint32_t count = 1000000;
74+
constexpr uint32_t step = UINT32_MAX / count;
75+
for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) {
76+
float x = as_float(v);
77+
if (isnan(x) || isinf(x))
78+
continue;
79+
EXPECT_TRUE(mpfr::equalsCos(x, __llvm_libc::cosf(x), tolerance));
80+
}
81+
}
82+
83+
// For small values, cos(x) is 1.
84+
TEST(CosfTest, SmallValues) {
85+
float x = as_float(0x17800000);
86+
float result = __llvm_libc::cosf(x);
87+
EXPECT_TRUE(mpfr::equalsCos(x, result, tolerance));
88+
EXPECT_EQ(FloatBits::One, as_uint32_bits(result));
89+
90+
x = as_float(0x00400000);
91+
result = __llvm_libc::cosf(x);
92+
EXPECT_TRUE(mpfr::equalsCos(x, result, tolerance));
93+
EXPECT_EQ(FloatBits::One, as_uint32_bits(result));
94+
}
95+
96+
// SDCOMP-26094: check cosf in the cases for which the range reducer
97+
// returns values furthest beyond its nominal upper bound of pi/4.
98+
TEST(CosfTest, SDCOMP_26094) {
99+
for (uint32_t v : sdcomp26094Values) {
100+
float x = as_float(v);
101+
EXPECT_TRUE(mpfr::equalsCos(x, __llvm_libc::cosf(x), tolerance));
102+
}
103+
}

‎libc/test/src/math/float.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===-- Single precision floating point test utils --------------*- 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_TEST_SRC_MATH_FLOAT_H
10+
#define LLVM_LIBC_TEST_SRC_MATH_FLOAT_H
11+
12+
#include "src/math/math_utils.h"
13+
14+
namespace __llvm_libc {
15+
namespace testing {
16+
17+
struct FloatBits {
18+
// The various NaN bit patterns here are just one of the many possible
19+
// patterns. The functions isQNan and isNegQNan can help understand why.
20+
21+
static const uint32_t QNan = 0x7fc00000;
22+
static const uint32_t NegQNan = 0xffc00000;
23+
24+
static const uint32_t SNan = 0x7f800001;
25+
static const uint32_t NegSNan = 0xff800001;
26+
27+
static bool isQNan(float f) {
28+
uint32_t bits = as_uint32_bits(f);
29+
return ((0x7fc00000 & bits) != 0) && ((0x80000000 & bits) == 0);
30+
}
31+
32+
static bool isNegQNan(float f) {
33+
uint32_t bits = as_uint32_bits(f);
34+
return 0xffc00000 & bits;
35+
}
36+
37+
static constexpr uint32_t Zero = 0x0;
38+
static constexpr uint32_t NegZero = 0x80000000;
39+
40+
static constexpr uint32_t Inf = 0x7f800000;
41+
static constexpr uint32_t NegInf = 0xff800000;
42+
43+
static constexpr uint32_t One = 0x3f800000;
44+
};
45+
46+
} // namespace testing
47+
} // namespace __llvm_libc
48+
49+
#endif // LLVM_LIBC_TEST_SRC_MATH_FLOAT_H

‎libc/test/src/math/sdcomp26094.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//===-- SDCOMP-26094 specific items -----------------------------*- 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_TEST_SRC_MATH_SDCOMP26094_H
10+
#define LLVM_LIBC_TEST_SRC_MATH_SDCOMP26094_H
11+
12+
#include "utils/CPP/Array.h"
13+
14+
namespace __llvm_libc {
15+
namespace testing {
16+
17+
static constexpr __llvm_libc::cpp::Array<uint32_t, 10> sdcomp26094Values{
18+
0x46427f1b, 0x4647e568, 0x46428bac, 0x4647f1f9, 0x4647fe8a,
19+
0x45d8d7f1, 0x45d371a4, 0x45ce0b57, 0x45d35882, 0x45cdf235,
20+
};
21+
22+
} // namespace testing
23+
} // namespace __llvm_libc
24+
25+
#endif // LLVM_LIBC_TEST_SRC_MATH_SDCOMP26094_H

‎libc/test/src/math/sincosf_test.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
//===-- Unittests for sincosf ---------------------------------------------===//
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 "include/math.h"
10+
#include "src/errno/llvmlibc_errno.h"
11+
#include "src/math/math_utils.h"
12+
#include "src/math/sincosf.h"
13+
#include "test/src/math/float.h"
14+
#include "test/src/math/sdcomp26094.h"
15+
#include "utils/CPP/Array.h"
16+
#include "utils/MPFRWrapper/MPFRUtils.h"
17+
#include "utils/UnitTest/Test.h"
18+
19+
#include <stdint.h>
20+
21+
using __llvm_libc::as_float;
22+
using __llvm_libc::as_uint32_bits;
23+
24+
using __llvm_libc::testing::FloatBits;
25+
using __llvm_libc::testing::sdcomp26094Values;
26+
27+
namespace mpfr = __llvm_libc::testing::mpfr;
28+
// 12 additional bits of precision over the base precision of a |float|
29+
// value.
30+
static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12,
31+
3 * 0x1000 / 4};
32+
33+
TEST(SinCosfTest, SpecialNumbers) {
34+
llvmlibc_errno = 0;
35+
float sin, cos;
36+
37+
__llvm_libc::sincosf(as_float(FloatBits::QNan), &sin, &cos);
38+
EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(cos)));
39+
EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(sin)));
40+
EXPECT_EQ(llvmlibc_errno, 0);
41+
42+
__llvm_libc::sincosf(as_float(FloatBits::NegQNan), &sin, &cos);
43+
EXPECT_TRUE(FloatBits::isNegQNan(as_uint32_bits(cos)));
44+
EXPECT_TRUE(FloatBits::isNegQNan(as_uint32_bits(sin)));
45+
EXPECT_EQ(llvmlibc_errno, 0);
46+
47+
__llvm_libc::sincosf(as_float(FloatBits::SNan), &sin, &cos);
48+
EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(cos)));
49+
EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(sin)));
50+
EXPECT_EQ(llvmlibc_errno, 0);
51+
52+
__llvm_libc::sincosf(as_float(FloatBits::NegSNan), &sin, &cos);
53+
EXPECT_TRUE(FloatBits::isNegQNan(as_uint32_bits(cos)));
54+
EXPECT_TRUE(FloatBits::isNegQNan(as_uint32_bits(sin)));
55+
EXPECT_EQ(llvmlibc_errno, 0);
56+
57+
__llvm_libc::sincosf(as_float(FloatBits::Zero), &sin, &cos);
58+
EXPECT_EQ(FloatBits::One, as_uint32_bits(cos));
59+
EXPECT_EQ(FloatBits::Zero, as_uint32_bits(sin));
60+
EXPECT_EQ(llvmlibc_errno, 0);
61+
62+
__llvm_libc::sincosf(as_float(FloatBits::NegZero), &sin, &cos);
63+
EXPECT_EQ(FloatBits::One, as_uint32_bits(cos));
64+
EXPECT_EQ(FloatBits::NegZero, as_uint32_bits(sin));
65+
EXPECT_EQ(llvmlibc_errno, 0);
66+
67+
llvmlibc_errno = 0;
68+
__llvm_libc::sincosf(as_float(FloatBits::Inf), &sin, &cos);
69+
EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(cos)));
70+
EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(sin)));
71+
EXPECT_EQ(llvmlibc_errno, EDOM);
72+
73+
llvmlibc_errno = 0;
74+
__llvm_libc::sincosf(as_float(FloatBits::NegInf), &sin, &cos);
75+
EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(cos)));
76+
EXPECT_TRUE(FloatBits::isQNan(as_uint32_bits(sin)));
77+
EXPECT_EQ(llvmlibc_errno, EDOM);
78+
}
79+
80+
TEST(SinCosfTest, InFloatRange) {
81+
constexpr uint32_t count = 1000000;
82+
constexpr uint32_t step = UINT32_MAX / count;
83+
for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) {
84+
float x = as_float(v);
85+
if (isnan(x) || isinf(x))
86+
continue;
87+
88+
float sin, cos;
89+
__llvm_libc::sincosf(x, &sin, &cos);
90+
EXPECT_TRUE(mpfr::equalsCos(x, cos, tolerance));
91+
EXPECT_TRUE(mpfr::equalsSin(x, sin, tolerance));
92+
}
93+
}
94+
95+
// For small values, cos(x) is 1 and sin(x) is x.
96+
TEST(SinCosfTest, SmallValues) {
97+
uint32_t bits = 0x17800000;
98+
float x = as_float(bits);
99+
float result_cos, result_sin;
100+
__llvm_libc::sincosf(x, &result_sin, &result_cos);
101+
EXPECT_TRUE(mpfr::equalsCos(x, result_cos, tolerance));
102+
EXPECT_TRUE(mpfr::equalsSin(x, result_sin, tolerance));
103+
EXPECT_EQ(FloatBits::One, as_uint32_bits(result_cos));
104+
EXPECT_EQ(bits, as_uint32_bits(result_sin));
105+
106+
bits = 0x00400000;
107+
x = as_float(bits);
108+
__llvm_libc::sincosf(x, &result_sin, &result_cos);
109+
EXPECT_TRUE(mpfr::equalsCos(x, result_cos, tolerance));
110+
EXPECT_TRUE(mpfr::equalsSin(x, result_sin, tolerance));
111+
EXPECT_EQ(FloatBits::One, as_uint32_bits(result_cos));
112+
EXPECT_EQ(bits, as_uint32_bits(result_sin));
113+
}
114+
115+
// SDCOMP-26094: check sinf in the cases for which the range reducer
116+
// returns values furthest beyond its nominal upper bound of pi/4.
117+
TEST(SinCosfTest, SDCOMP_26094) {
118+
for (uint32_t v : sdcomp26094Values) {
119+
float x = as_float(v);
120+
float sin, cos;
121+
__llvm_libc::sincosf(x, &sin, &cos);
122+
EXPECT_TRUE(mpfr::equalsCos(x, cos, tolerance));
123+
EXPECT_TRUE(mpfr::equalsSin(x, sin, tolerance));
124+
}
125+
}

‎libc/test/src/math/sinf_test.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//===-- Unittests for sinf ------------------------------------------------===//
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 "include/math.h"
10+
#include "src/errno/llvmlibc_errno.h"
11+
#include "src/math/math_utils.h"
12+
#include "src/math/sinf.h"
13+
#include "test/src/math/float.h"
14+
#include "test/src/math/sdcomp26094.h"
15+
#include "utils/CPP/Array.h"
16+
#include "utils/MPFRWrapper/MPFRUtils.h"
17+
#include "utils/UnitTest/Test.h"
18+
19+
#include <stdint.h>
20+
21+
using __llvm_libc::as_float;
22+
using __llvm_libc::as_uint32_bits;
23+
24+
using __llvm_libc::testing::FloatBits;
25+
using __llvm_libc::testing::sdcomp26094Values;
26+
27+
namespace mpfr = __llvm_libc::testing::mpfr;
28+
29+
// 12 additional bits of precision over the base precision of a |float|
30+
// value.
31+
static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12,
32+
3 * 0x1000 / 4};
33+
34+
TEST(SinfTest, SpecialNumbers) {
35+
llvmlibc_errno = 0;
36+
37+
EXPECT_TRUE(FloatBits::isQNan(
38+
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::QNan)))));
39+
EXPECT_EQ(llvmlibc_errno, 0);
40+
41+
EXPECT_TRUE(FloatBits::isNegQNan(
42+
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::NegQNan)))));
43+
EXPECT_EQ(llvmlibc_errno, 0);
44+
45+
EXPECT_TRUE(FloatBits::isQNan(
46+
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::SNan)))));
47+
EXPECT_EQ(llvmlibc_errno, 0);
48+
49+
EXPECT_TRUE(FloatBits::isNegQNan(
50+
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::NegSNan)))));
51+
EXPECT_EQ(llvmlibc_errno, 0);
52+
53+
EXPECT_EQ(FloatBits::Zero,
54+
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::Zero))));
55+
EXPECT_EQ(llvmlibc_errno, 0);
56+
57+
EXPECT_EQ(FloatBits::NegZero,
58+
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::NegZero))));
59+
EXPECT_EQ(llvmlibc_errno, 0);
60+
61+
llvmlibc_errno = 0;
62+
EXPECT_TRUE(FloatBits::isQNan(
63+
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::Inf)))));
64+
EXPECT_EQ(llvmlibc_errno, EDOM);
65+
66+
llvmlibc_errno = 0;
67+
EXPECT_TRUE(FloatBits::isNegQNan(
68+
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::NegInf)))));
69+
EXPECT_EQ(llvmlibc_errno, EDOM);
70+
}
71+
72+
TEST(SinfTest, InFloatRange) {
73+
constexpr uint32_t count = 1000000;
74+
constexpr uint32_t step = UINT32_MAX / count;
75+
for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) {
76+
float x = as_float(v);
77+
if (isnan(x) || isinf(x))
78+
continue;
79+
EXPECT_TRUE(mpfr::equalsSin(x, __llvm_libc::sinf(x), tolerance));
80+
}
81+
}
82+
83+
TEST(SinfTest, SpecificBitPatterns) {
84+
float x = as_float(0xc70d39a1);
85+
EXPECT_TRUE(mpfr::equalsSin(x, __llvm_libc::sinf(x), tolerance));
86+
}
87+
88+
// For small values, sin(x) is x.
89+
TEST(SinfTest, SmallValues) {
90+
uint32_t bits = 0x17800000;
91+
float x = as_float(bits);
92+
float result = __llvm_libc::sinf(x);
93+
EXPECT_TRUE(mpfr::equalsSin(x, result, tolerance));
94+
EXPECT_EQ(bits, as_uint32_bits(result));
95+
96+
bits = 0x00400000;
97+
x = as_float(bits);
98+
result = __llvm_libc::sinf(x);
99+
EXPECT_TRUE(mpfr::equalsSin(x, result, tolerance));
100+
EXPECT_EQ(bits, as_uint32_bits(result));
101+
}
102+
103+
// SDCOMP-26094: check sinf in the cases for which the range reducer
104+
// returns values furthest beyond its nominal upper bound of pi/4.
105+
TEST(SinfTest, SDCOMP_26094) {
106+
for (uint32_t v : sdcomp26094Values) {
107+
float x = as_float(v);
108+
EXPECT_TRUE(mpfr::equalsSin(x, __llvm_libc::sinf(x), tolerance));
109+
}
110+
}

‎libc/utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_subdirectory(CPP)
22
add_subdirectory(HdrGen)
3+
add_subdirectory(MPFRWrapper)
34
add_subdirectory(testutils)
45
add_subdirectory(UnitTest)
56
add_subdirectory(benchmarks)

‎libc/utils/MPFRWrapper/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
try_compile(
2+
LIBC_TESTS_CAN_USE_MPFR
3+
${CMAKE_CURRENT_BINARY_DIR}
4+
SOURCES
5+
${CMAKE_CURRENT_SOURCE_DIR}/check_mpfr.cpp
6+
LINK_LIBRARIES
7+
-lmpfr -lgmp
8+
)
9+
10+
if(LIBC_TESTS_CAN_USE_MPFR)
11+
add_library(libcMPFRWrapper
12+
MPFRUtils.cpp
13+
MPFRUtils.h
14+
)
15+
else()
16+
message(WARNING "Math tests using MPFR will be skipped.")
17+
endif()

‎libc/utils/MPFRWrapper/MPFRUtils.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//===-- Utils which wrap MPFR ---------------------------------------------===//
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 "MPFRUtils.h"
10+
11+
#include <iostream>
12+
#include <mpfr.h>
13+
14+
namespace __llvm_libc {
15+
namespace testing {
16+
namespace mpfr {
17+
18+
class MPFRNumber {
19+
// A precision value which allows sufficiently large additional
20+
// precision even compared to double precision floating point values.
21+
static constexpr unsigned int mpfrPrecision = 96;
22+
23+
mpfr_t value;
24+
25+
public:
26+
MPFRNumber() { mpfr_init2(value, mpfrPrecision); }
27+
28+
explicit MPFRNumber(float x) {
29+
mpfr_init2(value, mpfrPrecision);
30+
mpfr_set_flt(value, x, MPFR_RNDN);
31+
}
32+
33+
MPFRNumber(const MPFRNumber &other) {
34+
mpfr_set(value, other.value, MPFR_RNDN);
35+
}
36+
37+
~MPFRNumber() { mpfr_clear(value); }
38+
39+
// Returns true if |other| is within the tolerance value |t| of this
40+
// number.
41+
bool isEqual(const MPFRNumber &other, const Tolerance &t) {
42+
MPFRNumber tolerance(0.0);
43+
uint32_t bitMask = 1 << (t.width - 1);
44+
for (int exponent = -t.basePrecision; bitMask > 0; bitMask >>= 1) {
45+
--exponent;
46+
if (t.bits & bitMask) {
47+
MPFRNumber delta;
48+
mpfr_set_ui_2exp(delta.value, 1, exponent, MPFR_RNDN);
49+
mpfr_add(tolerance.value, tolerance.value, delta.value, MPFR_RNDN);
50+
}
51+
}
52+
53+
MPFRNumber difference;
54+
if (mpfr_cmp(value, other.value) >= 0)
55+
mpfr_sub(difference.value, value, other.value, MPFR_RNDN);
56+
else
57+
mpfr_sub(difference.value, other.value, value, MPFR_RNDN);
58+
59+
return mpfr_lessequal_p(difference.value, tolerance.value);
60+
}
61+
62+
// These functions are useful for debugging.
63+
float asFloat() const { return mpfr_get_flt(value, MPFR_RNDN); }
64+
double asDouble() const { return mpfr_get_d(value, MPFR_RNDN); }
65+
void dump(const char *msg) const { mpfr_printf("%s%.128Rf\n", msg, value); }
66+
67+
public:
68+
static MPFRNumber cos(float x) {
69+
MPFRNumber result;
70+
MPFRNumber mpfrX(x);
71+
mpfr_cos(result.value, mpfrX.value, MPFR_RNDN);
72+
return result;
73+
}
74+
75+
static MPFRNumber sin(float x) {
76+
MPFRNumber result;
77+
MPFRNumber mpfrX(x);
78+
mpfr_sin(result.value, mpfrX.value, MPFR_RNDN);
79+
return result;
80+
}
81+
};
82+
83+
bool equalsCos(float input, float libcOutput, const Tolerance &t) {
84+
MPFRNumber mpfrResult = MPFRNumber::cos(input);
85+
MPFRNumber libcResult(libcOutput);
86+
return mpfrResult.isEqual(libcResult, t);
87+
}
88+
89+
bool equalsSin(float input, float libcOutput, const Tolerance &t) {
90+
MPFRNumber mpfrResult = MPFRNumber::sin(input);
91+
MPFRNumber libcResult(libcOutput);
92+
return mpfrResult.isEqual(libcResult, t);
93+
}
94+
95+
} // namespace mpfr
96+
} // namespace testing
97+
} // namespace __llvm_libc

‎libc/utils/MPFRWrapper/MPFRUtils.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===-- MPFRUtils.h ---------------------------------------------*- 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_UTILS_TESTUTILS_MPFRUTILS_H
10+
#define LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H
11+
12+
#include <stdint.h>
13+
14+
namespace __llvm_libc {
15+
namespace testing {
16+
namespace mpfr {
17+
18+
struct Tolerance {
19+
// Number of bits used to represent the fractional
20+
// part of a value of type 'float'.
21+
static constexpr unsigned int floatPrecision = 23;
22+
23+
// Number of bits used to represent the fractional
24+
// part of a value of type 'double'.
25+
static constexpr unsigned int doublePrecision = 52;
26+
27+
// The base precision of the number. For example, for values of
28+
// type float, the base precision is the value |floatPrecision|.
29+
unsigned int basePrecision;
30+
31+
unsigned int width; // Number of valid LSB bits in |value|.
32+
33+
// The bits in the tolerance value. The tolerance value will be
34+
// sum(bits[width - i] * 2 ^ (- basePrecision - i)) for |i| in
35+
// range [1, width].
36+
uint32_t bits;
37+
};
38+
39+
// Return true if |libcOutput| is within the tolerance |t| of the cos(x)
40+
// value as evaluated by MPFR.
41+
bool equalsCos(float x, float libcOutput, const Tolerance &t);
42+
43+
// Return true if |libcOutput| is within the tolerance |t| of the sin(x)
44+
// value as evaluated by MPFR.
45+
bool equalsSin(float x, float libcOutput, const Tolerance &t);
46+
47+
} // namespace mpfr
48+
} // namespace testing
49+
} // namespace __llvm_libc
50+
51+
#endif // LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H

‎libc/utils/MPFRWrapper/check_mpfr.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include <mpfr.h>
2+
3+
int main() {
4+
mpfr_t x;
5+
mpfr_init(x);
6+
mpfr_clear(x);
7+
return 0;
8+
}

0 commit comments

Comments
 (0)
Please sign in to comment.