pxfm/sin_cosf/sinpif.rs
1/*
2 * // Copyright (c) Radzivon Bartoshyk 8/2025. All rights reserved.
3 * //
4 * // Redistribution and use in source and binary forms, with or without modification,
5 * // are permitted provided that the following conditions are met:
6 * //
7 * // 1. Redistributions of source code must retain the above copyright notice, this
8 * // list of conditions and the following disclaimer.
9 * //
10 * // 2. Redistributions in binary form must reproduce the above copyright notice,
11 * // this list of conditions and the following disclaimer in the documentation
12 * // and/or other materials provided with the distribution.
13 * //
14 * // 3. Neither the name of the copyright holder nor the names of its
15 * // contributors may be used to endorse or promote products derived from
16 * // this software without specific prior written permission.
17 * //
18 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29use crate::common::{f_fmla, is_integerf};
30use crate::polyeval::f_polyeval5;
31use crate::sin_cosf::argument_reduction_pi::ArgumentReducerPi;
32use crate::sin_cosf::sincosf_eval::{cospif_eval, sinpif_eval, sinpif_eval2};
33
34/// Computes sin(PI*x)
35///
36/// Max ULP 0.5
37#[inline]
38pub fn f_sinpif(x: f32) -> f32 {
39 let x_abs = x.to_bits() & 0x7fff_ffffu32;
40 let xd = x as f64;
41
42 // |x| <= 1/16
43 if x_abs <= 0x3d80_0000u32 {
44 // |x| < 0.0000009546391
45 if x_abs < 0x3580_2126u32 {
46 if x_abs == 0u32 {
47 // For signed zeros.
48 return x;
49 }
50 const PI: f64 = f64::from_bits(0x400921fb54442d18);
51 const MPI_E3_OVER_6: f64 = f64::from_bits(0xc014abbce625be53);
52
53 // Small values approximated with Taylor poly
54 // x = pi * x - pi^3*x^3/6
55 let x2 = xd * xd;
56 let p = f_fmla(x2, MPI_E3_OVER_6, PI);
57 return (xd * p) as f32;
58 }
59
60 let xsqr = xd * xd;
61
62 /*
63 Generated by Sollya:
64 d = [0, 1/16];
65 f_sin = sin(y*pi)/y;
66 Q = fpminimax(sin(y*pi)/y, [|0, 2, 4, 6, 8|], [|D...|], d, relative, floating);
67
68 See ./notes/sinpif.sollya
69 */
70 let p = f_polyeval5(
71 xsqr,
72 f64::from_bits(0x400921fb54442d18),
73 f64::from_bits(0xc014abbce625bbf2),
74 f64::from_bits(0x400466bc675e116a),
75 f64::from_bits(0xbfe32d2c0b62d41c),
76 f64::from_bits(0x3fb501ec4497cb7d),
77 );
78 return (xd * p) as f32;
79 }
80
81 // Numbers greater or equal to 2^23 are always integers or NaN
82 if x_abs >= 0x4b00_0000u32 || is_integerf(x) {
83 if x_abs >= 0x7f80_0000u32 {
84 return x + f32::NAN;
85 }
86 return if x.is_sign_negative() { -0. } else { 0. };
87 }
88
89 // We're computing sin(y) after argument reduction then return valid value
90 // based on quadrant
91
92 let reducer = ArgumentReducerPi { x: x as f64 };
93 let (y, k) = reducer.reduce_0p25();
94 // Decide based on quadrant what kernel function to use
95 (match k & 3 {
96 0 => sinpif_eval(y),
97 1 => cospif_eval(y),
98 2 => sinpif_eval(-y),
99 _ => -cospif_eval(y),
100 }) as f32
101}
102
103pub(crate) fn fast_sinpif(x: f32) -> f64 {
104 let x_abs = x.to_bits() & 0x7fff_ffffu32;
105 let xd = x as f64;
106
107 // |x| <= 1/16
108 if x_abs <= 0x3d80_0000u32 {
109 // |x| < 0.0000009546391
110 if x_abs < 0x3580_2126u32 {
111 if x_abs == 0u32 {
112 // For signed zeros.
113 return x as f64;
114 }
115 const PI: f64 = f64::from_bits(0x400921fb54442d18);
116 const MPI_E3_OVER_6: f64 = f64::from_bits(0xc014abbce625be53);
117
118 // Small values approximated with Taylor poly
119 // x = pi * x - pi^3*x^3/6
120 let x2 = xd * xd;
121 let p = f_fmla(x2, MPI_E3_OVER_6, PI);
122 return xd * p;
123 }
124
125 let xsqr = xd * xd;
126
127 /*
128 Generated by Sollya:
129 d = [0, 1/16];
130 f_sin = sin(y*pi)/y;
131 Q = fpminimax(sin(y*pi)/y, [|0, 2, 4, 6, 8|], [|D...|], d, relative, floating);
132
133 See ./notes/sinpif.sollya
134 */
135 let p = f_polyeval5(
136 xsqr,
137 f64::from_bits(0x400921fb54442d18),
138 f64::from_bits(0xc014abbce625bbf2),
139 f64::from_bits(0x400466bc675e116a),
140 f64::from_bits(0xbfe32d2c0b62d41c),
141 f64::from_bits(0x3fb501ec4497cb7d),
142 );
143 return xd * p;
144 }
145
146 let reducer = ArgumentReducerPi { x: x as f64 };
147 let (y, k) = reducer.reduce_0p25();
148 // Decide based on quadrant what kernel function to use
149 match k & 3 {
150 0 => sinpif_eval2(y),
151 1 => cospif_eval(y),
152 2 => sinpif_eval2(-y),
153 _ => -cospif_eval(y),
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_f_sinpif() {
163 assert_eq!(f_sinpif(3.), 0.);
164 assert_eq!(f_sinpif(1.12199515e-7), 3.524852e-7);
165 assert_eq!(f_sinpif(-0.31706), -0.83934295);
166 assert_eq!(f_sinpif(0.30706), 0.8218538);
167 assert_eq!(f_sinpif(115.30706), -0.82185423);
168 assert!(f_sinpif(f32::INFINITY).is_nan());
169 assert!(f_sinpif(f32::NEG_INFINITY).is_nan());
170 assert!(f_sinpif(f32::NAN).is_nan());
171 }
172}