[libc-commits] [libc] [libc][math][c23] Implemented sinpif function correctly rounded for all rounding modes. (PR #97149)
Hendrik Hübner via libc-commits
libc-commits at lists.llvm.org
Mon Jul 1 13:25:27 PDT 2024
https://github.com/HendrikHuebner updated https://github.com/llvm/llvm-project/pull/97149
>From 15e2975673183b611f8930424e83548b3ef6b12d Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Tue, 11 Jun 2024 11:28:39 +0200
Subject: [PATCH 01/17] Added sinpi declarations and test
---
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/docs/math/index.rst | 2 +-
libc/src/math/generic/CMakeLists.txt | 22 ++++
libc/src/math/generic/sinpif.cpp | 160 +++++++++++++++++++++++
libc/src/math/sinpif.h | 18 +++
libc/test/src/math/CMakeLists.txt | 16 +++
libc/test/src/math/sinpif_test.cpp | 127 ++++++++++++++++++
libc/utils/MPFRWrapper/MPFRUtils.cpp | 2 +
libc/utils/MPFRWrapper/MPFRUtils.h | 1 +
9 files changed, 348 insertions(+), 1 deletion(-)
create mode 100644 libc/src/math/generic/sinpif.cpp
create mode 100644 libc/src/math/sinpif.h
create mode 100644 libc/test/src/math/sinpif_test.cpp
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index e1922ca94b97e..25d14582f3d3e 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -514,6 +514,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.sincosf
libc.src.math.sinf
libc.src.math.sinhf
+ libc.src.math.sinpif
libc.src.math.sqrt
libc.src.math.sqrtf
libc.src.math.sqrtl
diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst
index 1bc75a9e0517f..d1877ce91dc41 100644
--- a/libc/docs/math/index.rst
+++ b/libc/docs/math/index.rst
@@ -330,7 +330,7 @@ Higher Math Functions
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| sinh | |check| | | | | | 7.12.5.5 | F.10.2.5 |
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
-| sinpi | | | | | | 7.12.4.13 | F.10.1.13 |
+| sinpi | |check| | | | | | 7.12.4.13 | F.10.1.13 |
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
| sqrt | |check| | |check| | |check| | | |check| | 7.12.7.10 | F.10.4.10 |
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 0e4893cd42ee1..0538d21cc328f 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -283,6 +283,28 @@ add_entrypoint_object(
-O3
)
+add_entrypoint_object(
+ sinpif
+ SRCS
+ sinpif.cpp
+ HDRS
+ ../sinpif.h
+ DEPENDS
+ .range_reduction
+ .sincosf_utils
+ libc.src.errno.errno
+ libc.src.__support.FPUtil.basic_operations
+ libc.src.__support.FPUtil.fenv_impl
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.FPUtil.fma
+ libc.src.__support.FPUtil.multiply_add
+ libc.src.__support.FPUtil.polyeval
+ libc.src.__support.FPUtil.rounding_mode
+ libc.src.__support.macros.optimization
+ COMPILE_OPTIONS
+ -O3
+)
+
add_entrypoint_object(
sincosf
SRCS
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
new file mode 100644
index 0000000000000..50c0494bb2233
--- /dev/null
+++ b/libc/src/math/generic/sinpif.cpp
@@ -0,0 +1,160 @@
+//===-- Single-precision sinpi 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 "src/math/sinpif.h"
+#include "sincosf_utils.h"
+#include "src/__support/FPUtil/BasicOperations.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/rounding_mode.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
+#include "src/__support/macros/properties/cpu_features.h" // LIBC_TARGET_CPU_HAS_FMA
+
+#include <errno.h>
+
+#if defined(LIBC_TARGET_CPU_HAS_FMA)
+#include "range_reduction_fma.h"
+#else
+#include "range_reduction.h"
+#endif
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
+ using FPBits = typename fputil::FPBits<float>;
+ FPBits xbits(x);
+
+ uint32_t x_u = xbits.uintval();
+ uint32_t x_abs = x_u & 0x7fff'ffffU;
+ double xd = static_cast<double>(x);
+
+ // Range reduction:
+ // For |x| > pi/32, we perform range reduction as follows:
+ // Find k and y such that:
+ // x = (k + y) * pi/32
+ // k is an integer
+ // |y| < 0.5
+ // For small range (|x| < 2^45 when FMA instructions are available, 2^22
+ // otherwise), this is done by performing:
+ // k = round(x * 32/pi)
+ // y = x * 32/pi - k
+ // For large range, we will omit all the higher parts of 32/pi such that the
+ // least significant bits of their full products with x are larger than 63,
+ // since sin((k + y + 64*i) * pi/32) = sin(x + i * 2pi) = sin(x).
+ //
+ // When FMA instructions are not available, we store the digits of 32/pi in
+ // chunks of 28-bit precision. This will make sure that the products:
+ // x * THIRTYTWO_OVER_PI_28[i] are all exact.
+ // When FMA instructions are available, we simply store the digits of 32/pi in
+ // chunks of doubles (53-bit of precision).
+ // So when multiplying by the largest values of single precision, the
+ // resulting output should be correct up to 2^(-208 + 128) ~ 2^-80. By the
+ // worst-case analysis of range reduction, |y| >= 2^-38, so this should give
+ // us more than 40 bits of accuracy. For the worst-case estimation of range
+ // reduction, see for instances:
+ // Elementary Functions by J-M. Muller, Chapter 11,
+ // Handbook of Floating-Point Arithmetic by J-M. Muller et. al.,
+ // Chapter 10.2.
+ //
+ // Once k and y are computed, we then deduce the answer by the sine of sum
+ // formula:
+ // sin(x) = sin((k + y)*pi/32)
+ // = sin(y*pi/32) * cos(k*pi/32) + cos(y*pi/32) * sin(k*pi/32)
+ // The values of sin(k*pi/32) and cos(k*pi/32) for k = 0..31 are precomputed
+ // and stored using a vector of 32 doubles. Sin(y*pi/32) and cos(y*pi/32) are
+ // computed using degree-7 and degree-6 minimax polynomials generated by
+ // Sollya respectively.
+
+ // |x| <= 1/16
+ if (LIBC_UNLIKELY(x_abs <= 0x3d80'0000U)) {
+
+ // |x| < 0x1.d12ed2p-12f
+ if (LIBC_UNLIKELY(x_abs < 0x39e8'9769U)) {
+ if (LIBC_UNLIKELY(x_abs == 0U)) {
+ // For signed zeros.
+ return x;
+ }
+ // When |x| < 2^-12, the relative error of the approximation sin(x) ~ x
+ // is:
+ // |sin(x) - x| / |sin(x)| < |x^3| / (6|x|)
+ // = x^2 / 6
+ // < 2^-25
+ // < epsilon(1)/2.
+ // So the correctly rounded values of sin(x) are:
+ // = x - sign(x)*eps(x) if rounding mode = FE_TOWARDZERO,
+ // or (rounding mode = FE_UPWARD and x is
+ // negative),
+ // = x otherwise.
+ // To simplify the rounding decision and make it more efficient, we use
+ // fma(x, -2^-25, x) instead.
+ // An exhaustive test shows that this formula work correctly for all
+ // rounding modes up to |x| < 0x1.c555dep-11f.
+ // Note: to use the formula x - 2^-25*x to decide the correct rounding, we
+ // do need fma(x, -2^-25, x) to prevent underflow caused by -2^-25*x when
+ // |x| < 2^-125. For targets without FMA instructions, we simply use
+ // double for intermediate results as it is more efficient than using an
+ // emulated version of FMA.
+#if defined(LIBC_TARGET_CPU_HAS_FMA)
+ return fputil::multiply_add(x, -0x1.0p-25f, x);
+#else
+ return static_cast<float>(fputil::multiply_add(xd, -0x1.0p-25, xd));
+#endif // LIBC_TARGET_CPU_HAS_FMAx
+ }
+
+ // |x| < pi/16.
+ double xsq = xd * xd;
+
+ // Degree-9 polynomial approximation:
+ // sin(x) ~ x + a_3 x^3 + a_5 x^5 + a_7 x^7 + a_9 x^9
+ // = x (1 + a_3 x^2 + ... + a_9 x^8)
+ // = x * P(x^2)
+ // generated by Sollya with the following commands:
+ // > display = hexadecimal;
+ // > Q = fpminimax(sin(x)/x, [|0, 2, 4, 6, 8|], [|1, D...|], [0, pi/16]);
+ double result =
+ fputil::polyeval(xsq, 1.0, -0x1.55555555554c6p-3, 0x1.1111111085e65p-7,
+ -0x1.a019f70fb4d4fp-13, 0x1.718d179815e74p-19);
+ return static_cast<float>(xd * result);
+ }
+
+ if (LIBC_UNLIKELY(x_abs == 0x4619'9998U)) { // x = 0x1.33333p13
+ float r = -0x1.63f4bap-2f;
+ int rounding = fputil::quick_get_round();
+ if ((rounding == FE_DOWNWARD && xbits.is_pos()) ||
+ (rounding == FE_UPWARD && xbits.is_neg()))
+ r = -0x1.63f4bcp-2f;
+
+ return xbits.is_neg() ? -r : r;
+ }
+
+ if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
+ if (x_abs == 0x7f80'0000U) {
+ fputil::set_errno_if_required(EDOM);
+ fputil::raise_except_if_required(FE_INVALID);
+ }
+ return x + FPBits::quiet_nan().get_val();
+ }
+
+ // Combine the results with the sine of sum formula:
+ // sin(x) = sin((k + y)*pi/32)
+ // = sin(y*pi/32) * cos(k*pi/32) + cos(y*pi/32) * sin(k*pi/32)
+ // = sin_y * cos_k + (1 + cosm1_y) * sin_k
+ // = sin_y * cos_k + (cosm1_y * sin_k + sin_k)
+ double sin_k, cos_k, sin_y, cosm1_y;
+
+ sincosf_eval(xd, x_abs, sin_k, cos_k, sin_y, cosm1_y);
+
+ return static_cast<float>(fputil::multiply_add(
+ sin_y, cos_k, fputil::multiply_add(cosm1_y, sin_k, sin_k)));
+}
+
+} // namespace LIBC_NAMESPACE
+
diff --git a/libc/src/math/sinpif.h b/libc/src/math/sinpif.h
new file mode 100644
index 0000000000000..a5bef435efae6
--- /dev/null
+++ b/libc/src/math/sinpif.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for sinpif --------------------------*- 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_SINPIF_H
+#define LLVM_LIBC_SRC_MATH_SINPIF_H
+
+namespace LIBC_NAMESPACE {
+
+float sinpif(float x);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_MATH_SINPIF_H
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 061feeadb40d5..35d3227316645 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -44,6 +44,22 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)
+add_fp_unittest(
+ sinpif_test
+ NEED_MPFR
+ SUITE
+ libc-math-unittests
+ SRCS
+ sinpif_test.cpp
+ HDRS
+ sdcomp26094.h
+ DEPENDS
+ libc.src.errno.errno
+ libc.src.math.sinpif
+ libc.src.__support.CPP.array
+ libc.src.__support.FPUtil.fp_bits
+)
+
add_fp_unittest(
sin_test
NEED_MPFR
diff --git a/libc/test/src/math/sinpif_test.cpp b/libc/test/src/math/sinpif_test.cpp
new file mode 100644
index 0000000000000..9c2b1210ea3bc
--- /dev/null
+++ b/libc/test/src/math/sinpif_test.cpp
@@ -0,0 +1,127 @@
+//===-- Unittests for sinpif ------------------------------------------------===//
+//
+// 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 "hdr/math_macros.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/errno/libc_errno.h"
+#include "src/math/sinpif.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "test/src/math/sdcomp26094.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+#include <errno.h>
+#include <stdint.h>
+
+using LlvmLibcSinpifTest = LIBC_NAMESPACE::testing::FPTest<float>;
+
+using LIBC_NAMESPACE::testing::SDCOMP26094_VALUES;
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+TEST_F(LlvmLibcSinpifTest, SpecialNumbers) {
+ LIBC_NAMESPACE::libc_errno = 0;
+
+ EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinpif(aNaN));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::sinpif(0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(-0.0f, LIBC_NAMESPACE::sinpif(-0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinpif(inf));
+ EXPECT_MATH_ERRNO(EDOM);
+
+ EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinpif(neg_inf));
+ EXPECT_MATH_ERRNO(EDOM);
+}
+
+TEST_F(LlvmLibcSinpifTest, InFloatRange) {
+ constexpr uint32_t COUNT = 100'000;
+ constexpr uint32_t STEP = UINT32_MAX / COUNT;
+ for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
+ float x = FPBits(v).get_val();
+ if (isnan(x) || isinf(x))
+ continue;
+ ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinpi, x,
+ LIBC_NAMESPACE::sinpif(x), 0.5);
+ }
+}
+
+TEST_F(LlvmLibcSinpifTest, SpecificBitPatterns) {
+ constexpr int N = 36;
+ constexpr uint32_t INPUTS[N] = {
+ 0x3f06'0a92U, // x = pi/6
+ 0x3f3a'dc51U, // x = 0x1.75b8a2p-1f
+ 0x3f49'0fdbU, // x = pi/4
+ 0x3f86'0a92U, // x = pi/3
+ 0x3fa7'832aU, // x = 0x1.4f0654p+0f
+ 0x3fc9'0fdbU, // x = pi/2
+ 0x4017'1973U, // x = 0x1.2e32e6p+1f
+ 0x4049'0fdbU, // x = pi
+ 0x4096'cbe4U, // x = 0x1.2d97c8p+2f
+ 0x40c9'0fdbU, // x = 2*pi
+ 0x433b'7490U, // x = 0x1.76e92p+7f
+ 0x437c'e5f1U, // x = 0x1.f9cbe2p+7f
+ 0x4619'9998U, // x = 0x1.33333p+13f
+ 0x474d'246fU, // x = 0x1.9a48dep+15f
+ 0x4afd'ece4U, // x = 0x1.fbd9c8p+22f
+ 0x4c23'32e9U, // x = 0x1.4665d2p+25f
+ 0x50a3'e87fU, // x = 0x1.47d0fep+34f
+ 0x5239'47f6U, // x = 0x1.728fecp+37f
+ 0x53b1'46a6U, // x = 0x1.628d4cp+40f
+ 0x55ca'fb2aU, // x = 0x1.95f654p+44f
+ 0x588e'f060U, // x = 0x1.1de0cp+50f
+ 0x5c07'bcd0U, // x = 0x1.0f79ap+57f
+ 0x5ebc'fddeU, // x = 0x1.79fbbcp+62f
+ 0x5fa6'eba7U, // x = 0x1.4dd74ep+64f
+ 0x61a4'0b40U, // x = 0x1.48168p+68f
+ 0x6386'134eU, // x = 0x1.0c269cp+72f
+ 0x6589'8498U, // x = 0x1.13093p+76f
+ 0x6600'0001U, // x = 0x1.000002p+77f
+ 0x664e'46e4U, // x = 0x1.9c8dc8p+77f
+ 0x66b0'14aaU, // x = 0x1.602954p+78f
+ 0x67a9'242bU, // x = 0x1.524856p+80f
+ 0x6a19'76f1U, // x = 0x1.32ede2p+85f
+ 0x6c55'da58U, // x = 0x1.abb4bp+89f
+ 0x6f79'be45U, // x = 0x1.f37c8ap+95f
+ 0x7276'69d4U, // x = 0x1.ecd3a8p+101f
+ 0x7758'4625U, // x = 0x1.b08c4ap+111f
+ };
+
+ for (int i = 0; i < N; ++i) {
+ float x = FPBits(INPUTS[i]).get_val();
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinpi, x,
+ LIBC_NAMESPACE::sinpif(x), 0.5);
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinpi, -x,
+ LIBC_NAMESPACE::sinpif(-x), 0.5);
+ }
+}
+
+// For small values, sin(x) is x.
+TEST_F(LlvmLibcSinpifTest, SmallValues) {
+ float x = FPBits(0x1780'0000U).get_val();
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinpi, x,
+ LIBC_NAMESPACE::sinpif(x), 0.5);
+
+ x = FPBits(0x0040'0000U).get_val();
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinpi, x,
+ LIBC_NAMESPACE::sinpif(x), 0.5);
+}
+
+// SDCOMP-26094: check sinpif in the cases for which the range reducer
+// returns values furthest beyond its nominal upper bound of pi/4.
+TEST_F(LlvmLibcSinpifTest, SDCOMP_26094) {
+ for (uint32_t v : SDCOMP26094_VALUES) {
+ float x = FPBits((v)).get_val();
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinpi, x,
+ LIBC_NAMESPACE::sinpif(x), 0.5);
+ }
+}
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index 0bfe49984e7d2..90a0d4bb6fc55 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -677,6 +677,8 @@ unary_operation(Operation op, InputType input, unsigned int precision,
return mpfrInput.roundeven();
case Operation::Sin:
return mpfrInput.sin();
+ case Operation::Sinpi:
+ return mpfrInput.sinpi();
case Operation::Sinh:
return mpfrInput.sinh();
case Operation::Sqrt:
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h
index 46f3375fd4b7e..11e323bf6881d 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.h
+++ b/libc/utils/MPFRWrapper/MPFRUtils.h
@@ -51,6 +51,7 @@ enum class Operation : int {
Round,
RoundEven,
Sin,
+ Sinpi,
Sinh,
Sqrt,
Tan,
>From b294cb10318f3960f5aed714d6b33c71cf6bb9ca Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Fri, 21 Jun 2024 13:26:29 +0200
Subject: [PATCH 02/17] Add mpfr sinpi function and update cmake
---
libc/src/math/CMakeLists.txt | 1 +
libc/src/math/generic/sinpif.cpp | 3 +++
libc/test/src/math/exhaustive/CMakeLists.txt | 16 ++++++++++++++++
libc/test/src/math/sinpif_test.cpp | 11 -----------
libc/utils/MPFRWrapper/MPFRUtils.cpp | 6 ++++++
5 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index fb0d971e88733..5200f06c90424 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -373,6 +373,7 @@ add_math_entrypoint_object(sincosf)
add_math_entrypoint_object(sin)
add_math_entrypoint_object(sinf)
+add_math_entrypoint_object(sinpif)
add_math_entrypoint_object(sinh)
add_math_entrypoint_object(sinhf)
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index 50c0494bb2233..f76b34c3d1b4d 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -101,6 +101,9 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
// do need fma(x, -2^-25, x) to prevent underflow caused by -2^-25*x when
// |x| < 2^-125. For targets without FMA instructions, we simply use
// double for intermediate results as it is more efficient than using an
+ double xdpi = xd * 3.1415926535897;
+ return static_cast<float>(fputil::multiply_add(xd, -0x1.65p-34, xdpi));
+
// emulated version of FMA.
#if defined(LIBC_TARGET_CPU_HAS_FMA)
return fputil::multiply_add(x, -0x1.0p-25f, x);
diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt
index fb3596c3378ff..412ca031d0e99 100644
--- a/libc/test/src/math/exhaustive/CMakeLists.txt
+++ b/libc/test/src/math/exhaustive/CMakeLists.txt
@@ -42,6 +42,22 @@ add_fp_unittest(
-lpthread
)
+add_fp_unittest(
+ sinpif_test
+ NO_RUN_POSTBUILD
+ NEED_MPFR
+ SUITE
+ libc_math_exhaustive_tests
+ SRCS
+ sinpif_test.cpp
+ DEPENDS
+ .exhaustive_test
+ libc.src.math.sinpif
+ libc.src.__support.FPUtil.fp_bits
+ LINK_LIBRARIES
+ -lpthread
+)
+
add_fp_unittest(
cosf_test
NO_RUN_POSTBUILD
diff --git a/libc/test/src/math/sinpif_test.cpp b/libc/test/src/math/sinpif_test.cpp
index 9c2b1210ea3bc..40bcb9541c5f5 100644
--- a/libc/test/src/math/sinpif_test.cpp
+++ b/libc/test/src/math/sinpif_test.cpp
@@ -43,17 +43,6 @@ TEST_F(LlvmLibcSinpifTest, SpecialNumbers) {
EXPECT_MATH_ERRNO(EDOM);
}
-TEST_F(LlvmLibcSinpifTest, InFloatRange) {
- constexpr uint32_t COUNT = 100'000;
- constexpr uint32_t STEP = UINT32_MAX / COUNT;
- for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) {
- float x = FPBits(v).get_val();
- if (isnan(x) || isinf(x))
- continue;
- ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinpi, x,
- LIBC_NAMESPACE::sinpif(x), 0.5);
- }
-}
TEST_F(LlvmLibcSinpifTest, SpecificBitPatterns) {
constexpr int N = 36;
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index 90a0d4bb6fc55..395028557a1bf 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -437,6 +437,12 @@ class MPFRNumber {
return result;
}
+ MPFRNumber sinpi() const {
+ MPFRNumber result(*this);
+ mpfr_sinpi(result.value, value, mpfr_rounding);
+ return result;
+ }
+
MPFRNumber sinh() const {
MPFRNumber result(*this);
mpfr_sinh(result.value, value, mpfr_rounding);
>From de3f7f48e9ede0a371acb58045f5a649acf260a6 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sat, 29 Jun 2024 00:05:46 +0200
Subject: [PATCH 03/17] Computed new polynom factors for sinpi function
---
libc/src/math/generic/range_reduction_fma.h | 10 ++++
libc/src/math/generic/sincosf_utils.h | 39 ++++++++++----
libc/src/math/generic/sinpif.cpp | 48 +++++++++--------
libc/test/src/math/exhaustive/sinpif_test.cpp | 26 ++++++++++
libc/test/src/math/sinpif_test.cpp | 51 -------------------
5 files changed, 92 insertions(+), 82 deletions(-)
create mode 100644 libc/test/src/math/exhaustive/sinpif_test.cpp
diff --git a/libc/src/math/generic/range_reduction_fma.h b/libc/src/math/generic/range_reduction_fma.h
index 82b4ae1c705e1..b65031311f0d9 100644
--- a/libc/src/math/generic/range_reduction_fma.h
+++ b/libc/src/math/generic/range_reduction_fma.h
@@ -38,6 +38,16 @@ LIBC_INLINE int64_t small_range_reduction(double x, double &y) {
return static_cast<int64_t>(kd);
}
+// Return k and y, where
+// k = round(x * 32) and y = (x * 32) - k.
+// => pi * x = (k + y) * pi / 32
+LIBC_INLINE int64_t small_range_reduction_mul_pi(double x, double &y) {
+ double kd = fputil::nearest_integer(x * 32);
+ y = fputil::fma(x, 32.0, -kd);
+
+ return static_cast<int64_t>(kd);
+}
+
// Return k and y, where
// k = round(x * 32 / pi) and y = (x * 32 / pi) - k.
// This is used for sinf, cosf, sincosf.
diff --git a/libc/src/math/generic/sincosf_utils.h b/libc/src/math/generic/sincosf_utils.h
index 904df4f4ed08e..3f5d2777f7cfa 100644
--- a/libc/src/math/generic/sincosf_utils.h
+++ b/libc/src/math/generic/sincosf_utils.h
@@ -11,14 +11,18 @@
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/macros/attributes.h"
#include "src/__support/macros/properties/cpu_features.h" // LIBC_TARGET_CPU_HAS_FMA
+#include <cstdint>
#if defined(LIBC_TARGET_CPU_HAS_FMA)
#include "range_reduction_fma.h"
// using namespace LIBC_NAMESPACE::fma;
using LIBC_NAMESPACE::fma::FAST_PASS_BOUND;
using LIBC_NAMESPACE::fma::large_range_reduction;
+using LIBC_NAMESPACE::fma::small_range_reduction_mul_pi;
using LIBC_NAMESPACE::fma::small_range_reduction;
+
#else
#include "range_reduction.h"
// using namespace LIBC_NAMESPACE::generic;
@@ -58,18 +62,9 @@ const double SIN_K_PI_OVER_32[64] = {
-0x1.917a6bc29b42cp-4,
};
-LIBC_INLINE void sincosf_eval(double xd, uint32_t x_abs, double &sin_k,
- double &cos_k, double &sin_y, double &cosm1_y) {
- int64_t k;
- double y;
-
- if (LIBC_LIKELY(x_abs < FAST_PASS_BOUND)) {
- k = small_range_reduction(xd, y);
- } else {
- fputil::FPBits<float> x_bits(x_abs);
- k = large_range_reduction(xd, x_bits.get_exponent(), y);
- }
+static LIBC_INLINE void sincosf_poly_eval(int64_t k, double y, double &sin_k,
+ double &cos_k, double &sin_y, double &cosm1_y) {
// After range reduction, k = round(x * 32 / pi) and y = (x * 32 / pi) - k.
// So k is an integer and -0.5 <= y <= 0.5.
// Then sin(x) = sin((k + y)*pi/32)
@@ -95,6 +90,28 @@ LIBC_INLINE void sincosf_eval(double xd, uint32_t x_abs, double &sin_k,
0x1.03c1f070c2e27p-18, -0x1.55cc84bd942p-30);
}
+LIBC_INLINE void sincosf_eval(double xd, uint32_t x_abs, double &sin_k,
+ double &cos_k, double &sin_y, double &cosm1_y) {
+ int64_t k;
+ double y;
+
+ if (LIBC_LIKELY(x_abs < FAST_PASS_BOUND)) {
+ k = small_range_reduction(xd, y);
+ } else {
+ fputil::FPBits<float> x_bits(x_abs);
+ k = large_range_reduction(xd, x_bits.get_exponent(), y);
+ }
+
+ sincosf_poly_eval(k, y, sin_k, cos_k, sin_y, cosm1_y);
+}
+
+LIBC_INLINE void sincospif_eval(double xd, double &sin_k, double &cos_k,
+ double &sin_y, double &cosm1_y) {
+ double y;
+ int64_t k = small_range_reduction_mul_pi(xd, y);
+ sincosf_poly_eval(k, y, sin_k, cos_k, sin_y, cosm1_y);
+}
+
} // namespace LIBC_NAMESPACE
#endif // LLVM_LIBC_SRC_MATH_GENERIC_SINCOSF_UTILS_H
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index f76b34c3d1b4d..dd0f3afb050b0 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -18,7 +18,9 @@
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
#include "src/__support/macros/properties/cpu_features.h" // LIBC_TARGET_CPU_HAS_FMA
+#include <cstdio>
#include <errno.h>
+#include <iostream>
#if defined(LIBC_TARGET_CPU_HAS_FMA)
#include "range_reduction_fma.h"
@@ -28,6 +30,23 @@
namespace LIBC_NAMESPACE {
+float tof(int i) {
+ return *(float * )&i;
+}
+
+int toi(float f) {
+ return *(int *)&f;
+}
+
+
+double dtof(long i) {
+ return *(double * )&i;
+}
+
+long dtoi(double f) {
+ return *(long *)&f;
+}
+
LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
using FPBits = typename fputil::FPBits<float>;
FPBits xbits(x);
@@ -76,8 +95,7 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
// |x| <= 1/16
if (LIBC_UNLIKELY(x_abs <= 0x3d80'0000U)) {
- // |x| < 0x1.d12ed2p-12f
- if (LIBC_UNLIKELY(x_abs < 0x39e8'9769U)) {
+ if (LIBC_UNLIKELY(x_abs < 0x34b0'0000U)) {
if (LIBC_UNLIKELY(x_abs == 0U)) {
// For signed zeros.
return x;
@@ -102,17 +120,16 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
// |x| < 2^-125. For targets without FMA instructions, we simply use
// double for intermediate results as it is more efficient than using an
double xdpi = xd * 3.1415926535897;
- return static_cast<float>(fputil::multiply_add(xd, -0x1.65p-34, xdpi));
+ return static_cast<float>(xdpi);
// emulated version of FMA.
#if defined(LIBC_TARGET_CPU_HAS_FMA)
- return fputil::multiply_add(x, -0x1.0p-25f, x);
#else
return static_cast<float>(fputil::multiply_add(xd, -0x1.0p-25, xd));
#endif // LIBC_TARGET_CPU_HAS_FMAx
}
- // |x| < pi/16.
+ // |x| < 1/16.
double xsq = xd * xd;
// Degree-9 polynomial approximation:
@@ -121,22 +138,13 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
// = x * P(x^2)
// generated by Sollya with the following commands:
// > display = hexadecimal;
- // > Q = fpminimax(sin(x)/x, [|0, 2, 4, 6, 8|], [|1, D...|], [0, pi/16]);
- double result =
- fputil::polyeval(xsq, 1.0, -0x1.55555555554c6p-3, 0x1.1111111085e65p-7,
- -0x1.a019f70fb4d4fp-13, 0x1.718d179815e74p-19);
+ // > Q = fpminimax(sin(pi * x)/x, [|0, 2, 4, 6, 8|], [|D...|], [0, 1/16]);
+ double result = fputil::polyeval(
+ xsq, 0x1.921fb54442d18p1, -0x1.4abbce625bbf2p2, 0x1.466bc675e116ap1,
+ -0x1.32d2c0b62d41cp-1, 0x1.501ec4497cb7dp-4);
return static_cast<float>(xd * result);
}
- if (LIBC_UNLIKELY(x_abs == 0x4619'9998U)) { // x = 0x1.33333p13
- float r = -0x1.63f4bap-2f;
- int rounding = fputil::quick_get_round();
- if ((rounding == FE_DOWNWARD && xbits.is_pos()) ||
- (rounding == FE_UPWARD && xbits.is_neg()))
- r = -0x1.63f4bcp-2f;
-
- return xbits.is_neg() ? -r : r;
- }
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
if (x_abs == 0x7f80'0000U) {
@@ -147,13 +155,13 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
}
// Combine the results with the sine of sum formula:
- // sin(x) = sin((k + y)*pi/32)
+ // sin(x * pi) = sin((k + y)*pi/32)
// = sin(y*pi/32) * cos(k*pi/32) + cos(y*pi/32) * sin(k*pi/32)
// = sin_y * cos_k + (1 + cosm1_y) * sin_k
// = sin_y * cos_k + (cosm1_y * sin_k + sin_k)
double sin_k, cos_k, sin_y, cosm1_y;
- sincosf_eval(xd, x_abs, sin_k, cos_k, sin_y, cosm1_y);
+ sincospif_eval(xd, sin_k, cos_k, sin_y, cosm1_y);
return static_cast<float>(fputil::multiply_add(
sin_y, cos_k, fputil::multiply_add(cosm1_y, sin_k, sin_k)));
diff --git a/libc/test/src/math/exhaustive/sinpif_test.cpp b/libc/test/src/math/exhaustive/sinpif_test.cpp
new file mode 100644
index 0000000000000..96870ed200d96
--- /dev/null
+++ b/libc/test/src/math/exhaustive/sinpif_test.cpp
@@ -0,0 +1,26 @@
+//===-- Exhaustive test for sinpif ------------------------------------------===//
+//
+// 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 "exhaustive_test.h"
+#include "src/math/sinpif.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+#include "mpfr.h"
+#include <sys/types.h>
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+using LlvmLibcSinpifExhaustiveTest =
+ LlvmLibcUnaryOpExhaustiveMathTest<float, mpfr::Operation::Sinpi,
+ LIBC_NAMESPACE::sinpif>;
+
+static constexpr uint32_t POS_START = 0x0000'0000U;
+static constexpr uint32_t POS_STOP = 0x7f80'0000U;
+
+TEST_F(LlvmLibcSinpifExhaustiveTest, PostiveRange) {
+ test_full_range_all_roundings(POS_START, POS_STOP);
+}
diff --git a/libc/test/src/math/sinpif_test.cpp b/libc/test/src/math/sinpif_test.cpp
index 40bcb9541c5f5..05f886ba8bfa8 100644
--- a/libc/test/src/math/sinpif_test.cpp
+++ b/libc/test/src/math/sinpif_test.cpp
@@ -43,57 +43,6 @@ TEST_F(LlvmLibcSinpifTest, SpecialNumbers) {
EXPECT_MATH_ERRNO(EDOM);
}
-
-TEST_F(LlvmLibcSinpifTest, SpecificBitPatterns) {
- constexpr int N = 36;
- constexpr uint32_t INPUTS[N] = {
- 0x3f06'0a92U, // x = pi/6
- 0x3f3a'dc51U, // x = 0x1.75b8a2p-1f
- 0x3f49'0fdbU, // x = pi/4
- 0x3f86'0a92U, // x = pi/3
- 0x3fa7'832aU, // x = 0x1.4f0654p+0f
- 0x3fc9'0fdbU, // x = pi/2
- 0x4017'1973U, // x = 0x1.2e32e6p+1f
- 0x4049'0fdbU, // x = pi
- 0x4096'cbe4U, // x = 0x1.2d97c8p+2f
- 0x40c9'0fdbU, // x = 2*pi
- 0x433b'7490U, // x = 0x1.76e92p+7f
- 0x437c'e5f1U, // x = 0x1.f9cbe2p+7f
- 0x4619'9998U, // x = 0x1.33333p+13f
- 0x474d'246fU, // x = 0x1.9a48dep+15f
- 0x4afd'ece4U, // x = 0x1.fbd9c8p+22f
- 0x4c23'32e9U, // x = 0x1.4665d2p+25f
- 0x50a3'e87fU, // x = 0x1.47d0fep+34f
- 0x5239'47f6U, // x = 0x1.728fecp+37f
- 0x53b1'46a6U, // x = 0x1.628d4cp+40f
- 0x55ca'fb2aU, // x = 0x1.95f654p+44f
- 0x588e'f060U, // x = 0x1.1de0cp+50f
- 0x5c07'bcd0U, // x = 0x1.0f79ap+57f
- 0x5ebc'fddeU, // x = 0x1.79fbbcp+62f
- 0x5fa6'eba7U, // x = 0x1.4dd74ep+64f
- 0x61a4'0b40U, // x = 0x1.48168p+68f
- 0x6386'134eU, // x = 0x1.0c269cp+72f
- 0x6589'8498U, // x = 0x1.13093p+76f
- 0x6600'0001U, // x = 0x1.000002p+77f
- 0x664e'46e4U, // x = 0x1.9c8dc8p+77f
- 0x66b0'14aaU, // x = 0x1.602954p+78f
- 0x67a9'242bU, // x = 0x1.524856p+80f
- 0x6a19'76f1U, // x = 0x1.32ede2p+85f
- 0x6c55'da58U, // x = 0x1.abb4bp+89f
- 0x6f79'be45U, // x = 0x1.f37c8ap+95f
- 0x7276'69d4U, // x = 0x1.ecd3a8p+101f
- 0x7758'4625U, // x = 0x1.b08c4ap+111f
- };
-
- for (int i = 0; i < N; ++i) {
- float x = FPBits(INPUTS[i]).get_val();
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinpi, x,
- LIBC_NAMESPACE::sinpif(x), 0.5);
- EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinpi, -x,
- LIBC_NAMESPACE::sinpif(-x), 0.5);
- }
-}
-
// For small values, sin(x) is x.
TEST_F(LlvmLibcSinpifTest, SmallValues) {
float x = FPBits(0x1780'0000U).get_val();
>From 26ff5472fb1861f272b497f86e391ccaf4e9ca19 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sat, 29 Jun 2024 00:20:07 +0200
Subject: [PATCH 04/17] Update factor for very small x
---
libc/src/math/generic/sinpif.cpp | 30 +++---------------------------
1 file changed, 3 insertions(+), 27 deletions(-)
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index dd0f3afb050b0..d42952ac08f43 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -95,38 +95,14 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
// |x| <= 1/16
if (LIBC_UNLIKELY(x_abs <= 0x3d80'0000U)) {
- if (LIBC_UNLIKELY(x_abs < 0x34b0'0000U)) {
+ if (LIBC_UNLIKELY(x_abs < 0x33FC'1538U)) {
if (LIBC_UNLIKELY(x_abs == 0U)) {
// For signed zeros.
return x;
}
- // When |x| < 2^-12, the relative error of the approximation sin(x) ~ x
- // is:
- // |sin(x) - x| / |sin(x)| < |x^3| / (6|x|)
- // = x^2 / 6
- // < 2^-25
- // < epsilon(1)/2.
- // So the correctly rounded values of sin(x) are:
- // = x - sign(x)*eps(x) if rounding mode = FE_TOWARDZERO,
- // or (rounding mode = FE_UPWARD and x is
- // negative),
- // = x otherwise.
- // To simplify the rounding decision and make it more efficient, we use
- // fma(x, -2^-25, x) instead.
- // An exhaustive test shows that this formula work correctly for all
- // rounding modes up to |x| < 0x1.c555dep-11f.
- // Note: to use the formula x - 2^-25*x to decide the correct rounding, we
- // do need fma(x, -2^-25, x) to prevent underflow caused by -2^-25*x when
- // |x| < 2^-125. For targets without FMA instructions, we simply use
- // double for intermediate results as it is more efficient than using an
- double xdpi = xd * 3.1415926535897;
- return static_cast<float>(xdpi);
- // emulated version of FMA.
-#if defined(LIBC_TARGET_CPU_HAS_FMA)
-#else
- return static_cast<float>(fputil::multiply_add(xd, -0x1.0p-25, xd));
-#endif // LIBC_TARGET_CPU_HAS_FMAx
+ double xdpi = xd * 0x1.921fb54442d18p1; // pi
+ return static_cast<float>(xdpi);
}
// |x| < 1/16.
>From ed5c6406f2c4d09543363e4450112f1ca18cf4f0 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sat, 29 Jun 2024 00:31:47 +0200
Subject: [PATCH 05/17] remove debug functions
---
libc/src/math/generic/sinpif.cpp | 17 -----------------
1 file changed, 17 deletions(-)
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index d42952ac08f43..5ee2de3d5c640 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -30,23 +30,6 @@
namespace LIBC_NAMESPACE {
-float tof(int i) {
- return *(float * )&i;
-}
-
-int toi(float f) {
- return *(int *)&f;
-}
-
-
-double dtof(long i) {
- return *(double * )&i;
-}
-
-long dtoi(double f) {
- return *(long *)&f;
-}
-
LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
using FPBits = typename fputil::FPBits<float>;
FPBits xbits(x);
>From e21f7fb25bf4551b87a26e6c576422be4cfea97a Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sat, 29 Jun 2024 00:48:28 +0200
Subject: [PATCH 06/17] Added smoke and unit test
---
libc/src/math/generic/sinpif.cpp | 8 +--
libc/test/src/math/exhaustive/sinpif_test.cpp | 9 ++++
libc/test/src/math/sinpif_test.cpp | 54 ++++++++++++++++++-
libc/test/src/math/smoke/CMakeLists.txt | 13 +++++
libc/test/src/math/smoke/sinpif_test.cpp | 38 +++++++++++++
5 files changed, 117 insertions(+), 5 deletions(-)
create mode 100644 libc/test/src/math/smoke/sinpif_test.cpp
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index 5ee2de3d5c640..2cedb6f29021b 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -78,13 +78,15 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
// |x| <= 1/16
if (LIBC_UNLIKELY(x_abs <= 0x3d80'0000U)) {
- if (LIBC_UNLIKELY(x_abs < 0x33FC'1538U)) {
+ if (LIBC_UNLIKELY(x_abs < 0x33CD'01D7U)) {
if (LIBC_UNLIKELY(x_abs == 0U)) {
// For signed zeros.
return x;
}
-
- double xdpi = xd * 0x1.921fb54442d18p1; // pi
+
+ // For very small values we can approximate sinpi(x) with x * pi
+ // An exhaustive test shows that this is accurate for |x| < 9.546391 × 10-8
+ double xdpi = xd * 0x1.921fb54442d18p1;
return static_cast<float>(xdpi);
}
diff --git a/libc/test/src/math/exhaustive/sinpif_test.cpp b/libc/test/src/math/exhaustive/sinpif_test.cpp
index 96870ed200d96..3c74c2872bef0 100644
--- a/libc/test/src/math/exhaustive/sinpif_test.cpp
+++ b/libc/test/src/math/exhaustive/sinpif_test.cpp
@@ -21,6 +21,15 @@ using LlvmLibcSinpifExhaustiveTest =
static constexpr uint32_t POS_START = 0x0000'0000U;
static constexpr uint32_t POS_STOP = 0x7f80'0000U;
+// Range: [0, Inf]
TEST_F(LlvmLibcSinpifExhaustiveTest, PostiveRange) {
test_full_range_all_roundings(POS_START, POS_STOP);
}
+
+// Range: [-Inf, 0]
+static constexpr uint32_t NEG_START = 0xb000'0000U;
+static constexpr uint32_t NEG_STOP = 0xff80'0000U;
+
+TEST_F(LlvmLibcSinpifExhaustiveTest, NegativeRange) {
+ test_full_range_all_roundings(NEG_START, NEG_STOP);
+}
diff --git a/libc/test/src/math/sinpif_test.cpp b/libc/test/src/math/sinpif_test.cpp
index 05f886ba8bfa8..15f25b86574ac 100644
--- a/libc/test/src/math/sinpif_test.cpp
+++ b/libc/test/src/math/sinpif_test.cpp
@@ -43,7 +43,57 @@ TEST_F(LlvmLibcSinpifTest, SpecialNumbers) {
EXPECT_MATH_ERRNO(EDOM);
}
-// For small values, sin(x) is x.
+TEST_F(LlvmLibcSinpifTest, SpecificBitPatterns) {
+ constexpr int N = 36;
+ constexpr uint32_t INPUTS[N] = {
+ 0x3f06'0a92U, // x = pi/6
+ 0x3f3a'dc51U, // x = 0x1.75b8a2p-1f
+ 0x3f49'0fdbU, // x = pi/4
+ 0x3f86'0a92U, // x = pi/3
+ 0x3fa7'832aU, // x = 0x1.4f0654p+0f
+ 0x3fc9'0fdbU, // x = pi/2
+ 0x4017'1973U, // x = 0x1.2e32e6p+1f
+ 0x4049'0fdbU, // x = pi
+ 0x4096'cbe4U, // x = 0x1.2d97c8p+2f
+ 0x40c9'0fdbU, // x = 2*pi
+ 0x433b'7490U, // x = 0x1.76e92p+7f
+ 0x437c'e5f1U, // x = 0x1.f9cbe2p+7f
+ 0x4619'9998U, // x = 0x1.33333p+13f
+ 0x474d'246fU, // x = 0x1.9a48dep+15f
+ 0x4afd'ece4U, // x = 0x1.fbd9c8p+22f
+ 0x4c23'32e9U, // x = 0x1.4665d2p+25f
+ 0x50a3'e87fU, // x = 0x1.47d0fep+34f
+ 0x5239'47f6U, // x = 0x1.728fecp+37f
+ 0x53b1'46a6U, // x = 0x1.628d4cp+40f
+ 0x55ca'fb2aU, // x = 0x1.95f654p+44f
+ 0x588e'f060U, // x = 0x1.1de0cp+50f
+ 0x5c07'bcd0U, // x = 0x1.0f79ap+57f
+ 0x5ebc'fddeU, // x = 0x1.79fbbcp+62f
+ 0x5fa6'eba7U, // x = 0x1.4dd74ep+64f
+ 0x61a4'0b40U, // x = 0x1.48168p+68f
+ 0x6386'134eU, // x = 0x1.0c269cp+72f
+ 0x6589'8498U, // x = 0x1.13093p+76f
+ 0x6600'0001U, // x = 0x1.000002p+77f
+ 0x664e'46e4U, // x = 0x1.9c8dc8p+77f
+ 0x66b0'14aaU, // x = 0x1.602954p+78f
+ 0x67a9'242bU, // x = 0x1.524856p+80f
+ 0x6a19'76f1U, // x = 0x1.32ede2p+85f
+ 0x6c55'da58U, // x = 0x1.abb4bp+89f
+ 0x6f79'be45U, // x = 0x1.f37c8ap+95f
+ 0x7276'69d4U, // x = 0x1.ecd3a8p+101f
+ 0x7758'4625U, // x = 0x1.b08c4ap+111f
+ };
+
+ for (int i = 0; i < N; ++i) {
+ float x = FPBits(INPUTS[i]).get_val();
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinpi, x,
+ LIBC_NAMESPACE::sinpif(x), 0.5);
+ EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinpi, -x,
+ LIBC_NAMESPACE::sinpif(-x), 0.5);
+ }
+}
+
+// For small values, sinpi(x) is pi * x.
TEST_F(LlvmLibcSinpifTest, SmallValues) {
float x = FPBits(0x1780'0000U).get_val();
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Sinpi, x,
@@ -54,7 +104,7 @@ TEST_F(LlvmLibcSinpifTest, SmallValues) {
LIBC_NAMESPACE::sinpif(x), 0.5);
}
-// SDCOMP-26094: check sinpif in the cases for which the range reducer
+// SDCOMP-26094: check sinfpi in the cases for which the range reducer
// returns values furthest beyond its nominal upper bound of pi/4.
TEST_F(LlvmLibcSinpifTest, SDCOMP_26094) {
for (uint32_t v : SDCOMP26094_VALUES) {
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index e3d8a14191ad2..5bd0c887cb53c 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -27,6 +27,19 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)
+add_fp_unittest(
+ sinpif_test
+ SUITE
+ libc-math-smoke-tests
+ SRCS
+ sinpif_test.cpp
+ DEPENDS
+ libc.src.errno.errno
+ libc.src.math.sinpif
+ libc.src.__support.CPP.array
+ libc.src.__support.FPUtil.fp_bits
+)
+
add_fp_unittest(
sincosf_test
SUITE
diff --git a/libc/test/src/math/smoke/sinpif_test.cpp b/libc/test/src/math/smoke/sinpif_test.cpp
new file mode 100644
index 0000000000000..9b3b6a6948345
--- /dev/null
+++ b/libc/test/src/math/smoke/sinpif_test.cpp
@@ -0,0 +1,38 @@
+//===-- Unittests for sinpif ------------------------------------------------===//
+//
+// 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 "hdr/math_macros.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/errno/libc_errno.h"
+#include "src/math/sinpif.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+#include <errno.h>
+#include <stdint.h>
+
+using LlvmLibcSinpifTest = LIBC_NAMESPACE::testing::FPTest<float>;
+
+TEST_F(LlvmLibcSinpifTest, SpecialNumbers) {
+ LIBC_NAMESPACE::libc_errno = 0;
+
+ EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinpif(aNaN));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::sinpif(0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(-0.0f, LIBC_NAMESPACE::sinpif(-0.0f));
+ EXPECT_MATH_ERRNO(0);
+
+ EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinpif(inf));
+ EXPECT_MATH_ERRNO(EDOM);
+
+ EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinpif(neg_inf));
+ EXPECT_MATH_ERRNO(EDOM);
+}
>From 6e0ddaf4dd59c3defd457c8d720ecb23c1828edd Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sat, 29 Jun 2024 00:56:51 +0200
Subject: [PATCH 07/17] Removed unnecessary headers
---
libc/src/math/generic/range_reduction_fma.h | 2 +-
libc/src/math/generic/sincosf_utils.h | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/libc/src/math/generic/range_reduction_fma.h b/libc/src/math/generic/range_reduction_fma.h
index b65031311f0d9..a270fdc0033f2 100644
--- a/libc/src/math/generic/range_reduction_fma.h
+++ b/libc/src/math/generic/range_reduction_fma.h
@@ -43,7 +43,7 @@ LIBC_INLINE int64_t small_range_reduction(double x, double &y) {
// => pi * x = (k + y) * pi / 32
LIBC_INLINE int64_t small_range_reduction_mul_pi(double x, double &y) {
double kd = fputil::nearest_integer(x * 32);
- y = fputil::fma(x, 32.0, -kd);
+ y = fputil::fma<double>(x, 32.0, -kd);
return static_cast<int64_t>(kd);
}
diff --git a/libc/src/math/generic/sincosf_utils.h b/libc/src/math/generic/sincosf_utils.h
index 3f5d2777f7cfa..5d93b287057a1 100644
--- a/libc/src/math/generic/sincosf_utils.h
+++ b/libc/src/math/generic/sincosf_utils.h
@@ -11,9 +11,7 @@
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
-#include "src/__support/macros/attributes.h"
#include "src/__support/macros/properties/cpu_features.h" // LIBC_TARGET_CPU_HAS_FMA
-#include <cstdint>
#if defined(LIBC_TARGET_CPU_HAS_FMA)
#include "range_reduction_fma.h"
>From f14de706cb136b3bc777951f153e711a46776bdb Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sat, 29 Jun 2024 09:45:43 +0200
Subject: [PATCH 08/17] Update explaination comments for sinpif
---
libc/src/math/generic/sinpif.cpp | 29 ++++++-----------------------
1 file changed, 6 insertions(+), 23 deletions(-)
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index 2cedb6f29021b..5008e1cdab19d 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -1,4 +1,4 @@
-//===-- Single-precision sinpi function -------------------------------------===//
+//===-- Single-precision sinpif function -------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -41,34 +41,17 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
// Range reduction:
// For |x| > pi/32, we perform range reduction as follows:
// Find k and y such that:
- // x = (k + y) * pi/32
+ // x = (k + y) * 1/32
// k is an integer
// |y| < 0.5
// For small range (|x| < 2^45 when FMA instructions are available, 2^22
// otherwise), this is done by performing:
- // k = round(x * 32/pi)
- // y = x * 32/pi - k
- // For large range, we will omit all the higher parts of 32/pi such that the
- // least significant bits of their full products with x are larger than 63,
- // since sin((k + y + 64*i) * pi/32) = sin(x + i * 2pi) = sin(x).
- //
- // When FMA instructions are not available, we store the digits of 32/pi in
- // chunks of 28-bit precision. This will make sure that the products:
- // x * THIRTYTWO_OVER_PI_28[i] are all exact.
- // When FMA instructions are available, we simply store the digits of 32/pi in
- // chunks of doubles (53-bit of precision).
- // So when multiplying by the largest values of single precision, the
- // resulting output should be correct up to 2^(-208 + 128) ~ 2^-80. By the
- // worst-case analysis of range reduction, |y| >= 2^-38, so this should give
- // us more than 40 bits of accuracy. For the worst-case estimation of range
- // reduction, see for instances:
- // Elementary Functions by J-M. Muller, Chapter 11,
- // Handbook of Floating-Point Arithmetic by J-M. Muller et. al.,
- // Chapter 10.2.
+ // k = round(x * 32)
+ // y = x * 32 - k
//
// Once k and y are computed, we then deduce the answer by the sine of sum
// formula:
- // sin(x) = sin((k + y)*pi/32)
+ // sin(x * pi) = sin((k + y)*pi/32)
// = sin(y*pi/32) * cos(k*pi/32) + cos(y*pi/32) * sin(k*pi/32)
// The values of sin(k*pi/32) and cos(k*pi/32) for k = 0..31 are precomputed
// and stored using a vector of 32 doubles. Sin(y*pi/32) and cos(y*pi/32) are
@@ -94,7 +77,7 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
double xsq = xd * xd;
// Degree-9 polynomial approximation:
- // sin(x) ~ x + a_3 x^3 + a_5 x^5 + a_7 x^7 + a_9 x^9
+ // sinpi(x) ~ x + a_3 x^3 + a_5 x^5 + a_7 x^7 + a_9 x^9
// = x (1 + a_3 x^2 + ... + a_9 x^8)
// = x * P(x^2)
// generated by Sollya with the following commands:
>From d5b2ab6b5e48712912a1f8b6f8c50e5fa53106d5 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sat, 29 Jun 2024 20:29:29 +0200
Subject: [PATCH 09/17] Fix formatting and clean up code
---
libc/config/darwin/arm/entrypoints.txt | 1 +
libc/config/linux/aarch64/entrypoints.txt | 1 +
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/src/math/generic/range_reduction_fma.h | 11 --------
libc/src/math/generic/sincosf_utils.h | 17 ++++++++++---
libc/src/math/generic/sinpif.cpp | 25 +++++--------------
libc/test/src/math/exhaustive/sinpif_test.cpp | 5 ++--
libc/test/src/math/sinpif_test.cpp | 3 ++-
libc/test/src/math/smoke/sinpif_test.cpp | 5 ++--
9 files changed, 30 insertions(+), 39 deletions(-)
diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt
index 4d5536318724d..cb4603c79c79c 100644
--- a/libc/config/darwin/arm/entrypoints.txt
+++ b/libc/config/darwin/arm/entrypoints.txt
@@ -230,6 +230,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.sinhf
libc.src.math.sin
libc.src.math.sinf
+ libc.src.math.sinpif
libc.src.math.sqrt
libc.src.math.sqrtf
libc.src.math.sqrtl
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 8a26536cea9a0..c26e25070dcd9 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -485,6 +485,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.sincosf
libc.src.math.sinf
libc.src.math.sinhf
+ libc.src.math.sinpif
libc.src.math.sqrt
libc.src.math.sqrtf
libc.src.math.sqrtl
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index ee8b3d531637e..51d85eed9ff16 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -493,6 +493,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.sincosf
libc.src.math.sinf
libc.src.math.sinhf
+ libc.src.math.sinpif
libc.src.math.sqrt
libc.src.math.sqrtf
libc.src.math.sqrtl
diff --git a/libc/src/math/generic/range_reduction_fma.h b/libc/src/math/generic/range_reduction_fma.h
index a270fdc0033f2..97b9e3d0f89db 100644
--- a/libc/src/math/generic/range_reduction_fma.h
+++ b/libc/src/math/generic/range_reduction_fma.h
@@ -12,7 +12,6 @@
#include "src/__support/FPUtil/FMA.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/nearest_integer.h"
-#include "src/__support/common.h"
namespace LIBC_NAMESPACE {
@@ -38,16 +37,6 @@ LIBC_INLINE int64_t small_range_reduction(double x, double &y) {
return static_cast<int64_t>(kd);
}
-// Return k and y, where
-// k = round(x * 32) and y = (x * 32) - k.
-// => pi * x = (k + y) * pi / 32
-LIBC_INLINE int64_t small_range_reduction_mul_pi(double x, double &y) {
- double kd = fputil::nearest_integer(x * 32);
- y = fputil::fma<double>(x, 32.0, -kd);
-
- return static_cast<int64_t>(kd);
-}
-
// Return k and y, where
// k = round(x * 32 / pi) and y = (x * 32 / pi) - k.
// This is used for sinf, cosf, sincosf.
diff --git a/libc/src/math/generic/sincosf_utils.h b/libc/src/math/generic/sincosf_utils.h
index 5d93b287057a1..f20fb6a05a324 100644
--- a/libc/src/math/generic/sincosf_utils.h
+++ b/libc/src/math/generic/sincosf_utils.h
@@ -18,7 +18,6 @@
// using namespace LIBC_NAMESPACE::fma;
using LIBC_NAMESPACE::fma::FAST_PASS_BOUND;
using LIBC_NAMESPACE::fma::large_range_reduction;
-using LIBC_NAMESPACE::fma::small_range_reduction_mul_pi;
using LIBC_NAMESPACE::fma::small_range_reduction;
#else
@@ -60,9 +59,9 @@ const double SIN_K_PI_OVER_32[64] = {
-0x1.917a6bc29b42cp-4,
};
-
static LIBC_INLINE void sincosf_poly_eval(int64_t k, double y, double &sin_k,
- double &cos_k, double &sin_y, double &cosm1_y) {
+ double &cos_k, double &sin_y,
+ double &cosm1_y) {
// After range reduction, k = round(x * 32 / pi) and y = (x * 32 / pi) - k.
// So k is an integer and -0.5 <= y <= 0.5.
// Then sin(x) = sin((k + y)*pi/32)
@@ -103,10 +102,20 @@ LIBC_INLINE void sincosf_eval(double xd, uint32_t x_abs, double &sin_k,
sincosf_poly_eval(k, y, sin_k, cos_k, sin_y, cosm1_y);
}
+// Return k and y, where
+// k = round(x * 32) and y = (x * 32) - k.
+// => pi * x = (k + y) * pi / 32
+static LIBC_INLINE int64_t range_reduction_sincospi(double x, double &y) {
+ double kd = fputil::nearest_integer(x * 32);
+ y = fputil::multiply_add<double>(x, 32.0, -kd);
+
+ return static_cast<int64_t>(kd);
+}
+
LIBC_INLINE void sincospif_eval(double xd, double &sin_k, double &cos_k,
double &sin_y, double &cosm1_y) {
double y;
- int64_t k = small_range_reduction_mul_pi(xd, y);
+ int64_t k = range_reduction_sincospi(xd, y);
sincosf_poly_eval(k, y, sin_k, cos_k, sin_y, cosm1_y);
}
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index 5008e1cdab19d..fa4be82e0a254 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -1,4 +1,5 @@
-//===-- Single-precision sinpif function -------------------------------------===//
+//===-- Single-precision sinpif function
+//-------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -8,25 +9,12 @@
#include "src/math/sinpif.h"
#include "sincosf_utils.h"
-#include "src/__support/FPUtil/BasicOperations.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/multiply_add.h"
-#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
-#include "src/__support/macros/properties/cpu_features.h" // LIBC_TARGET_CPU_HAS_FMA
-
-#include <cstdio>
-#include <errno.h>
-#include <iostream>
-
-#if defined(LIBC_TARGET_CPU_HAS_FMA)
-#include "range_reduction_fma.h"
-#else
-#include "range_reduction.h"
-#endif
namespace LIBC_NAMESPACE {
@@ -66,10 +54,11 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
// For signed zeros.
return x;
}
-
+
// For very small values we can approximate sinpi(x) with x * pi
- // An exhaustive test shows that this is accurate for |x| < 9.546391 × 10-8
- double xdpi = xd * 0x1.921fb54442d18p1;
+ // An exhaustive test shows that this is accurate for |x| < 9.546391 ×
+ // 10-8
+ double xdpi = xd * 0x1.921fb54442d18p1;
return static_cast<float>(xdpi);
}
@@ -89,7 +78,6 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
return static_cast<float>(xd * result);
}
-
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
if (x_abs == 0x7f80'0000U) {
fputil::set_errno_if_required(EDOM);
@@ -112,4 +100,3 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
}
} // namespace LIBC_NAMESPACE
-
diff --git a/libc/test/src/math/exhaustive/sinpif_test.cpp b/libc/test/src/math/exhaustive/sinpif_test.cpp
index 3c74c2872bef0..f255b0cb82982 100644
--- a/libc/test/src/math/exhaustive/sinpif_test.cpp
+++ b/libc/test/src/math/exhaustive/sinpif_test.cpp
@@ -1,4 +1,5 @@
-//===-- Exhaustive test for sinpif ------------------------------------------===//
+//===-- Exhaustive test for sinpif
+//------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -7,9 +8,9 @@
//===----------------------------------------------------------------------===//
#include "exhaustive_test.h"
+#include "mpfr.h"
#include "src/math/sinpif.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
-#include "mpfr.h"
#include <sys/types.h>
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
diff --git a/libc/test/src/math/sinpif_test.cpp b/libc/test/src/math/sinpif_test.cpp
index 15f25b86574ac..87e6d0dc211c0 100644
--- a/libc/test/src/math/sinpif_test.cpp
+++ b/libc/test/src/math/sinpif_test.cpp
@@ -1,4 +1,5 @@
-//===-- Unittests for sinpif ------------------------------------------------===//
+//===-- Unittests for sinpif
+//------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/libc/test/src/math/smoke/sinpif_test.cpp b/libc/test/src/math/smoke/sinpif_test.cpp
index 9b3b6a6948345..c22f0c4866475 100644
--- a/libc/test/src/math/smoke/sinpif_test.cpp
+++ b/libc/test/src/math/smoke/sinpif_test.cpp
@@ -1,4 +1,5 @@
-//===-- Unittests for sinpif ------------------------------------------------===//
+//===-- Unittests for sinpif
+//------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -13,7 +14,7 @@
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
-#include <errno.h>
+#include "hdr/errno_macros.h"
#include <stdint.h>
using LlvmLibcSinpifTest = LIBC_NAMESPACE::testing::FPTest<float>;
>From 2d57357df1283ae03c1911e50fa494f3a85cca80 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sat, 29 Jun 2024 20:38:16 +0200
Subject: [PATCH 10/17] Remove more unnecessary includes
---
libc/test/src/math/sinpif_test.cpp | 4 ----
libc/test/src/math/smoke/sinpif_test.cpp | 4 ----
2 files changed, 8 deletions(-)
diff --git a/libc/test/src/math/sinpif_test.cpp b/libc/test/src/math/sinpif_test.cpp
index 87e6d0dc211c0..dbb85e8f15379 100644
--- a/libc/test/src/math/sinpif_test.cpp
+++ b/libc/test/src/math/sinpif_test.cpp
@@ -7,16 +7,12 @@
//
//===----------------------------------------------------------------------===//
-#include "hdr/math_macros.h"
-#include "src/__support/FPUtil/FPBits.h"
#include "src/errno/libc_errno.h"
#include "src/math/sinpif.h"
#include "test/UnitTest/FPMatcher.h"
-#include "test/UnitTest/Test.h"
#include "test/src/math/sdcomp26094.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
-#include <errno.h>
#include <stdint.h>
using LlvmLibcSinpifTest = LIBC_NAMESPACE::testing::FPTest<float>;
diff --git a/libc/test/src/math/smoke/sinpif_test.cpp b/libc/test/src/math/smoke/sinpif_test.cpp
index c22f0c4866475..4cc8b4da934eb 100644
--- a/libc/test/src/math/smoke/sinpif_test.cpp
+++ b/libc/test/src/math/smoke/sinpif_test.cpp
@@ -7,14 +7,10 @@
//
//===----------------------------------------------------------------------===//
-#include "hdr/math_macros.h"
-#include "src/__support/FPUtil/FPBits.h"
#include "src/errno/libc_errno.h"
#include "src/math/sinpif.h"
#include "test/UnitTest/FPMatcher.h"
-#include "test/UnitTest/Test.h"
-#include "hdr/errno_macros.h"
#include <stdint.h>
using LlvmLibcSinpifTest = LIBC_NAMESPACE::testing::FPTest<float>;
>From 878a6e654bfdd78403d6452cb022d590644a233b Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sat, 29 Jun 2024 20:44:12 +0200
Subject: [PATCH 11/17] Fix CMake
---
libc/src/math/generic/CMakeLists.txt | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 0538d21cc328f..633b4628d3f63 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -290,16 +290,13 @@ add_entrypoint_object(
HDRS
../sinpif.h
DEPENDS
- .range_reduction
.sincosf_utils
- libc.src.errno.errno
- libc.src.__support.FPUtil.basic_operations
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.fma
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.polyeval
- libc.src.__support.FPUtil.rounding_mode
+ libc.src.__support.common
libc.src.__support.macros.optimization
COMPILE_OPTIONS
-O3
>From 1940d73af11f9ef33a533b8daf11d04e4891b295 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sat, 29 Jun 2024 20:50:58 +0200
Subject: [PATCH 12/17] Fix formatting
---
libc/src/math/generic/sinpif.cpp | 2 +-
libc/src/math/sinpif.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index fa4be82e0a254..0bf0ae4170e58 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -14,7 +14,7 @@
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/common.h"
-#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
+#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
namespace LIBC_NAMESPACE {
diff --git a/libc/src/math/sinpif.h b/libc/src/math/sinpif.h
index a5bef435efae6..63d481945f470 100644
--- a/libc/src/math/sinpif.h
+++ b/libc/src/math/sinpif.h
@@ -1,4 +1,4 @@
-//===-- Implementation header for sinpif --------------------------*- C++ -*-===//
+//===-- Implementation header for sinpif ------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
>From e3bb63261f1584634c4d4192e612c3c720c13dd0 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sat, 29 Jun 2024 20:56:01 +0200
Subject: [PATCH 13/17] Fix comment formatting
---
libc/src/math/generic/sinpif.cpp | 3 +--
libc/test/src/math/exhaustive/sinpif_test.cpp | 3 +--
libc/test/src/math/sinpif_test.cpp | 3 +--
libc/test/src/math/smoke/sinpif_test.cpp | 3 +--
4 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index 0bf0ae4170e58..353597f131471 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -1,5 +1,4 @@
-//===-- Single-precision sinpif function
-//-------------------------------------===//
+//===-- Single-precision sinpif function ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/libc/test/src/math/exhaustive/sinpif_test.cpp b/libc/test/src/math/exhaustive/sinpif_test.cpp
index f255b0cb82982..8bc1d81eb7e3d 100644
--- a/libc/test/src/math/exhaustive/sinpif_test.cpp
+++ b/libc/test/src/math/exhaustive/sinpif_test.cpp
@@ -1,5 +1,4 @@
-//===-- Exhaustive test for sinpif
-//------------------------------------------===//
+//===-- Exhaustive test for sinpif ----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/libc/test/src/math/sinpif_test.cpp b/libc/test/src/math/sinpif_test.cpp
index dbb85e8f15379..ffb1556ce6cd3 100644
--- a/libc/test/src/math/sinpif_test.cpp
+++ b/libc/test/src/math/sinpif_test.cpp
@@ -1,5 +1,4 @@
-//===-- Unittests for sinpif
-//------------------------------------------------===//
+//===-- Unittests for sinpif ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/libc/test/src/math/smoke/sinpif_test.cpp b/libc/test/src/math/smoke/sinpif_test.cpp
index 4cc8b4da934eb..d2211872d3a18 100644
--- a/libc/test/src/math/smoke/sinpif_test.cpp
+++ b/libc/test/src/math/smoke/sinpif_test.cpp
@@ -1,5 +1,4 @@
-//===-- Unittests for sinpif
-//------------------------------------------------===//
+//===-- Unittests for sinpif ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
>From 86d06c72b553bdfa63a651f3094fdd8bf4473861 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sat, 29 Jun 2024 23:06:24 +0200
Subject: [PATCH 14/17] Make mpfr sinpi util compatible with older verison
---
libc/utils/MPFRWrapper/MPFRUtils.cpp | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index 395028557a1bf..1efe36068797b 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -18,7 +18,9 @@
#include "test/UnitTest/FPMatcher.h"
#include "hdr/math_macros.h"
+#include <iostream>
#include <memory>
+#include <ostream>
#include <stdint.h>
#include "mpfr_inc.h"
@@ -439,7 +441,10 @@ class MPFRNumber {
MPFRNumber sinpi() const {
MPFRNumber result(*this);
- mpfr_sinpi(result.value, value, mpfr_rounding);
+ MPFRNumber value_pi(0.0, 1280);
+ mpfr_const_pi(value_pi.value, MPFR_RNDN);
+ mpfr_mul(value_pi.value, value_pi.value, value, MPFR_RNDN);
+ mpfr_sin(result.value, value_pi.value, mpfr_rounding);
return result;
}
>From 2c3cbb9d0c0a627b804df95586b62ce60b3830db Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sun, 30 Jun 2024 14:54:17 +0200
Subject: [PATCH 15/17] Fix sinpi for signed zeros
---
libc/src/math/generic/sinpif.cpp | 4 ++++
libc/test/src/math/sinpif_test.cpp | 15 +++++++++++++++
libc/utils/MPFRWrapper/MPFRUtils.cpp | 13 ++++++++-----
3 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index 353597f131471..bdf0bfaf907ac 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -10,6 +10,7 @@
#include "sincosf_utils.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/ManipulationFunctions.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/common.h"
@@ -94,6 +95,9 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
sincospif_eval(xd, sin_k, cos_k, sin_y, cosm1_y);
+ if (LIBC_UNLIKELY(sin_y == 0 && sin_k == 0))
+ return fputil::copysign(0.0f, x);
+
return static_cast<float>(fputil::multiply_add(
sin_y, cos_k, fputil::multiply_add(cosm1_y, sin_k, sin_k)));
}
diff --git a/libc/test/src/math/sinpif_test.cpp b/libc/test/src/math/sinpif_test.cpp
index ffb1556ce6cd3..c171e4aa87961 100644
--- a/libc/test/src/math/sinpif_test.cpp
+++ b/libc/test/src/math/sinpif_test.cpp
@@ -109,3 +109,18 @@ TEST_F(LlvmLibcSinpifTest, SDCOMP_26094) {
LIBC_NAMESPACE::sinpif(x), 0.5);
}
}
+
+// sinpi(-n) = -0.0
+// sinpi(+n) = +0.0
+TEST_F(LlvmLibcSinpifTest, SignedZeros) {
+ EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif(-0x99));
+ EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif(-0x1p+43));
+ EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif(-0x1.4p+64));
+ EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif(-0x1.cp+106));
+ EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif(-0x1.cp+21));
+ EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif(0x99));
+ EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif(0x1p+43));
+ EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif(0x1.4p+64));
+ EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif(0x1.cp+106));
+ EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif(0x1.cp+21));
+}
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index 1efe36068797b..bf35a73b3dbe8 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -15,12 +15,7 @@
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/fpbits_str.h"
#include "src/__support/macros/properties/types.h"
-#include "test/UnitTest/FPMatcher.h"
-#include "hdr/math_macros.h"
-#include <iostream>
-#include <memory>
-#include <ostream>
#include <stdint.h>
#include "mpfr_inc.h"
@@ -441,10 +436,18 @@ class MPFRNumber {
MPFRNumber sinpi() const {
MPFRNumber result(*this);
+
+#if MPFR_VERSION_MAJOR > 4 || \
+ (MPFR_VERSION_MAJOR == 4 && MPFR_VERSION_MINOR >= 2)
+
+ mpfr_sinpi(result.value, value, mpfr_rounding);
+#else
MPFRNumber value_pi(0.0, 1280);
mpfr_const_pi(value_pi.value, MPFR_RNDN);
mpfr_mul(value_pi.value, value_pi.value, value, MPFR_RNDN);
mpfr_sin(result.value, value_pi.value, mpfr_rounding);
+#endif
+
return result;
}
>From 35304a2812ce37a698f860486ec8c4f466f5e005 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sun, 30 Jun 2024 14:57:43 +0200
Subject: [PATCH 16/17] Formatting
---
libc/utils/MPFRWrapper/MPFRUtils.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index bf35a73b3dbe8..921fed84beab9 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -437,7 +437,7 @@ class MPFRNumber {
MPFRNumber sinpi() const {
MPFRNumber result(*this);
-#if MPFR_VERSION_MAJOR > 4 || \
+#if MPFR_VERSION_MAJOR > 4 || \
(MPFR_VERSION_MAJOR == 4 && MPFR_VERSION_MINOR >= 2)
mpfr_sinpi(result.value, value, mpfr_rounding);
>From 264f7e6bf23ea4d0862edebb17616988deece528 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Mon, 1 Jul 2024 22:24:55 +0200
Subject: [PATCH 17/17] Improve smoke test and add early return for integers
---
libc/src/math/generic/sinpif.cpp | 11 +++++++----
libc/test/src/math/sinpif_test.cpp | 2 +-
libc/test/src/math/smoke/sinpif_test.cpp | 9 +++++++++
3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/libc/src/math/generic/sinpif.cpp b/libc/src/math/generic/sinpif.cpp
index bdf0bfaf907ac..99a60426a68b3 100644
--- a/libc/src/math/generic/sinpif.cpp
+++ b/libc/src/math/generic/sinpif.cpp
@@ -10,7 +10,6 @@
#include "sincosf_utils.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
-#include "src/__support/FPUtil/ManipulationFunctions.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/common.h"
@@ -77,7 +76,7 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
-0x1.32d2c0b62d41cp-1, 0x1.501ec4497cb7dp-4);
return static_cast<float>(xd * result);
}
-
+
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
if (x_abs == 0x7f80'0000U) {
fputil::set_errno_if_required(EDOM);
@@ -86,17 +85,21 @@ LLVM_LIBC_FUNCTION(float, sinpif, (float x)) {
return x + FPBits::quiet_nan().get_val();
}
+ // Numbers greater or equal to 2^23 are always integers => sinpi(x) = 0
+ if (LIBC_UNLIKELY(x_abs >= 0x4B00'0000))
+ return FPBits::zero(xbits.sign()).get_val();
+
+
// Combine the results with the sine of sum formula:
// sin(x * pi) = sin((k + y)*pi/32)
// = sin(y*pi/32) * cos(k*pi/32) + cos(y*pi/32) * sin(k*pi/32)
// = sin_y * cos_k + (1 + cosm1_y) * sin_k
// = sin_y * cos_k + (cosm1_y * sin_k + sin_k)
double sin_k, cos_k, sin_y, cosm1_y;
-
sincospif_eval(xd, sin_k, cos_k, sin_y, cosm1_y);
if (LIBC_UNLIKELY(sin_y == 0 && sin_k == 0))
- return fputil::copysign(0.0f, x);
+ return FPBits::zero(xbits.sign()).get_val();
return static_cast<float>(fputil::multiply_add(
sin_y, cos_k, fputil::multiply_add(cosm1_y, sin_k, sin_k)));
diff --git a/libc/test/src/math/sinpif_test.cpp b/libc/test/src/math/sinpif_test.cpp
index c171e4aa87961..d00fd77d288c6 100644
--- a/libc/test/src/math/sinpif_test.cpp
+++ b/libc/test/src/math/sinpif_test.cpp
@@ -118,7 +118,7 @@ TEST_F(LlvmLibcSinpifTest, SignedZeros) {
EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif(-0x1.4p+64));
EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif(-0x1.cp+106));
EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif(-0x1.cp+21));
- EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif(0x99));
+ EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif(-0x9999));
EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif(0x1p+43));
EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif(0x1.4p+64));
EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif(0x1.cp+106));
diff --git a/libc/test/src/math/smoke/sinpif_test.cpp b/libc/test/src/math/smoke/sinpif_test.cpp
index d2211872d3a18..0918294ab3611 100644
--- a/libc/test/src/math/smoke/sinpif_test.cpp
+++ b/libc/test/src/math/smoke/sinpif_test.cpp
@@ -32,3 +32,12 @@ TEST_F(LlvmLibcSinpifTest, SpecialNumbers) {
EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::sinpif(neg_inf));
EXPECT_MATH_ERRNO(EDOM);
}
+
+TEST_F(LlvmLibcSinpifTest, Integers) {
+ EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif(-0x420));
+ EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif(-0x1p+43));
+ EXPECT_FP_EQ(-0.0, LIBC_NAMESPACE::sinpif(-0x1.4p+64));
+ EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif(0x420));
+ EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif(0x1.cp+106));
+ EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::sinpif(0x1.cp+21));
+}
More information about the libc-commits
mailing list