Files
clang-p2996/libc/src/math/generic/sinpif.cpp
wldfngrs fdf20941a8 [libc][math] Fix signaling NaN handling for math functions. (#133347)
Add tests for signaling NaNs, and fix function behavior for handling
signaling NaN input.

Fixes https://github.com/llvm/llvm-project/issues/124812
2025-04-08 15:23:38 +02:00

118 lines
3.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//===-- 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.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/math/sinpif.h"
#include "sincosf_utils.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/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
namespace LIBC_NAMESPACE_DECL {
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| > 1/32, we perform range reduction as follows:
// Find k and y such that:
// x = (k + y) * 1/32
// k is an integer
// |y| < 0.5
//
// This is done by performing:
// 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 * 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
// computed using degree-7 and degree-6 minimax polynomials generated by
// Sollya respectively.
// |x| <= 1/16
if (LIBC_UNLIKELY(x_abs <= 0x3d80'0000U)) {
if (LIBC_UNLIKELY(x_abs < 0x33CD'01D7U)) {
if (LIBC_UNLIKELY(x_abs == 0U)) {
// 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;
return static_cast<float>(xdpi);
}
// |x| < 1/16.
double xsq = xd * xd;
// Degree-9 polynomial approximation:
// 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:
// > display = hexadecimal;
// > 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);
}
// Numbers greater or equal to 2^23 are always integers or NaN
if (LIBC_UNLIKELY(x_abs >= 0x4B00'0000)) {
// check for NaN values
if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) {
if (xbits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
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();
}
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 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)));
}
} // namespace LIBC_NAMESPACE_DECL