pxfm/sin_cosf/sincosf_eval.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::polyeval::{f_estrin_polyeval7, f_polyeval3, f_polyeval4, f_polyeval6};
30use crate::sin_cosf::argument_reduction::ArgumentReducer;
31use crate::sin_cosf::argument_reduction_pi::ArgumentReducerPi;
32
33/**
34Generated in SageMath:
35
36```text
37print("[")
38for k in range(64):
39 k = RealField(150)(k) * RealField(150).pi() / RealField(150)(32)
40 print(double_to_hex(k.sin()) + ",")
41print("];")
42```
43**/
44static SIN_K_PI_OVER32: [u64; 64] = [
45 0x0000000000000000,
46 0x3fb917a6bc29b42c,
47 0x3fc8f8b83c69a60b,
48 0x3fd294062ed59f06,
49 0x3fd87de2a6aea963,
50 0x3fde2b5d3806f63b,
51 0x3fe1c73b39ae68c8,
52 0x3fe44cf325091dd6,
53 0x3fe6a09e667f3bcd,
54 0x3fe8bc806b151741,
55 0x3fea9b66290ea1a3,
56 0x3fec38b2f180bdb1,
57 0x3fed906bcf328d46,
58 0x3fee9f4156c62dda,
59 0x3fef6297cff75cb0,
60 0x3fefd88da3d12526,
61 0x3ff0000000000000,
62 0x3fefd88da3d12526,
63 0x3fef6297cff75cb0,
64 0x3fee9f4156c62dda,
65 0x3fed906bcf328d46,
66 0x3fec38b2f180bdb1,
67 0x3fea9b66290ea1a3,
68 0x3fe8bc806b151741,
69 0x3fe6a09e667f3bcd,
70 0x3fe44cf325091dd6,
71 0x3fe1c73b39ae68c8,
72 0x3fde2b5d3806f63b,
73 0x3fd87de2a6aea963,
74 0x3fd294062ed59f06,
75 0x3fc8f8b83c69a60b,
76 0x3fb917a6bc29b42c,
77 0xb69f77598338bfdf,
78 0xbfb917a6bc29b42c,
79 0xbfc8f8b83c69a60b,
80 0xbfd294062ed59f06,
81 0xbfd87de2a6aea963,
82 0xbfde2b5d3806f63b,
83 0xbfe1c73b39ae68c8,
84 0xbfe44cf325091dd6,
85 0xbfe6a09e667f3bcd,
86 0xbfe8bc806b151741,
87 0xbfea9b66290ea1a3,
88 0xbfec38b2f180bdb1,
89 0xbfed906bcf328d46,
90 0xbfee9f4156c62dda,
91 0xbfef6297cff75cb0,
92 0xbfefd88da3d12526,
93 0xbff0000000000000,
94 0xbfefd88da3d12526,
95 0xbfef6297cff75cb0,
96 0xbfee9f4156c62dda,
97 0xbfed906bcf328d46,
98 0xbfec38b2f180bdb1,
99 0xbfea9b66290ea1a3,
100 0xbfe8bc806b151741,
101 0xbfe6a09e667f3bcd,
102 0xbfe44cf325091dd6,
103 0xbfe1c73b39ae68c8,
104 0xbfde2b5d3806f63b,
105 0xbfd87de2a6aea963,
106 0xbfd294062ed59f06,
107 0xbfc8f8b83c69a60b,
108 0xbfb917a6bc29b42c,
109];
110
111pub(crate) struct SinCosf {
112 pub(crate) sin_k: f64,
113 pub(crate) cos_k: f64,
114 pub(crate) sin_y: f64,
115 pub(crate) cosm1_y: f64,
116}
117
118#[inline]
119pub(crate) fn sincosf_eval(x: f64, x_abs: u32) -> SinCosf {
120 let argument_reduction = ArgumentReducer { x, x_abs };
121
122 let (y, k) = argument_reduction.reduce();
123
124 let y_sqr = y * y;
125
126 // After range reduction, k = round(x * 32 / pi) and y = (x * 32 / pi) - k.
127 // So k is an integer and -0.5 <= y <= 0.5.
128 // Then sin(x) = sin((k + y)*pi/32)
129 // = sin(y*pi/32) * cos(k*pi/32) + cos(y*pi/32) * sin(k*pi/32)
130
131 let sin_k = f64::from_bits(SIN_K_PI_OVER32[((k as u64) & 63) as usize]);
132 // cos(k * pi/32) = sin(k * pi/32 + pi/2) = sin((k + 16) * pi/32).
133 // cos_k = cos(k * pi/32)
134 let cos_k = f64::from_bits(SIN_K_PI_OVER32[((k.wrapping_add(16) as u64) & 63) as usize]);
135
136 // Degree-6 minimax even polynomial for sin(y*pi/32)/y generated by Sollya
137 // with:
138 // > Q = fpminimax(sin(y*pi/32)/y, [|0, 2, 4, 6|], [|D...|], [0, 0.5]);
139 //
140 // See ./notes/sincosf.sollya
141 let sin_y = y * f_polyeval4(
142 y_sqr,
143 f64::from_bits(0x3fb921fb54442d18),
144 f64::from_bits(0xbf24abbce625abb1),
145 f64::from_bits(0x3e7466bc624f2776),
146 f64::from_bits(0xbdb32c3a619d4a7e),
147 );
148 // Degree-6 minimax even polynomial for cos(y*pi/32) generated by Sollya with:
149 // > P = fpminimax(cos(x*pi/32), [|0, 2, 4, 6|], [|1, D...|], [0, 0.5]);
150 // Note that cosm1_y = cos(y*pi/32) - 1.
151 //
152 // See ./notes/sincosf.sollya
153 let cosm1_y = y_sqr
154 * f_polyeval3(
155 y_sqr,
156 f64::from_bits(0xbf73bd3cc9be430b),
157 f64::from_bits(0x3ed03c1f070c2e27),
158 f64::from_bits(0xbe155cc84bd94200),
159 );
160
161 SinCosf {
162 sin_k,
163 cos_k,
164 sin_y,
165 cosm1_y,
166 }
167}
168
169#[inline]
170pub(crate) fn sincospif_eval(x: f64) -> SinCosf {
171 let argument_reduction = ArgumentReducerPi { x };
172
173 let (y, k) = argument_reduction.reduce();
174
175 let y_sqr = y * y;
176
177 // After range reduction, k = round(x * 32 / pi) and y = (x * 32 / pi) - k.
178 // So k is an integer and -0.5 <= y <= 0.5.
179 // Then sin(x) = sin((k + y)*pi/32)
180 // = sin(y*pi/32) * cos(k*pi/32) + cos(y*pi/32) * sin(k*pi/32)
181
182 let sin_k = f64::from_bits(SIN_K_PI_OVER32[((k as u64) & 63) as usize]);
183 // cos(k * pi/32) = sin(k * pi/32 + pi/2) = sin((k + 16) * pi/32).
184 // cos_k = cos(k * pi/32)
185 let cos_k = f64::from_bits(SIN_K_PI_OVER32[((k.wrapping_add(16) as u64) & 63) as usize]);
186
187 // Degree-6 minimax even polynomial for sin(y*pi/32)/y generated by Sollya
188 // with:
189 // > Q = fpminimax(sin(y*pi/32)/y, [|0, 2, 4, 6|], [|D...|], [0, 0.5]);
190 //
191 // See ./notes/sincosf.sollya
192 let sin_y = y * f_polyeval4(
193 y_sqr,
194 f64::from_bits(0x3fb921fb54442d18),
195 f64::from_bits(0xbf24abbce625abb1),
196 f64::from_bits(0x3e7466bc624f2776),
197 f64::from_bits(0xbdb32c3a619d4a7e),
198 );
199 // Degree-6 minimax even polynomial for cos(y*pi/32) generated by Sollya with:
200 // > P = fpminimax(cos(x*pi/32), [|0, 2, 4, 6|], [|1, D...|], [0, 0.5]);
201 // Note that cosm1_y = cos(y*pi/32) - 1.
202 //
203 // See ./notes/sincosf.sollya
204 let cosm1_y = y_sqr
205 * f_polyeval3(
206 y_sqr,
207 f64::from_bits(0xbf73bd3cc9be430b),
208 f64::from_bits(0x3ed03c1f070c2e27),
209 f64::from_bits(0xbe155cc84bd94200),
210 );
211
212 SinCosf {
213 sin_k,
214 cos_k,
215 sin_y,
216 cosm1_y,
217 }
218}
219
220/**
221Poly for sin(pi*y) on [-0.25; 0.25].
222Generated by Sollya:
223```text
224d = [0,0.25];
225f_sin = sin(y*pi)/y;
226Q = fpminimax(f_sin, [|0, 2, 4, 6, 8, 10|], [|D...|], d, relative, floating);
227```
228See ./notes/sinpif_0p25.sollya
229**/
230#[inline]
231pub(crate) fn sinpif_eval(y: f64) -> f64 {
232 let y2 = y * y;
233 f_polyeval6(
234 y2,
235 f64::from_bits(0x400921fb54442cf8),
236 f64::from_bits(0xc014abbce6257778),
237 f64::from_bits(0x400466bc670fa464),
238 f64::from_bits(0xbfe32d2c627f47da),
239 f64::from_bits(0x3fb5071be0a2d3da),
240 f64::from_bits(0xbf7dd4e0ae5b9fcd),
241 ) * y
242}
243
244/**
245Poly for sin(pi*y) on [-0.25; 0.25].
246Generated by Sollya:
247```text
248d = [0,0.25];
249f_sin = sin(y*pi)/y;
250Q = fpminimax(f_sin, [|0, 2, 4, 6, 8, 10, 12|], [|D...|], d, relative, floating);
251```
252See ./notes/sinpif_0p25_2.sollya
253**/
254#[inline]
255pub(crate) fn sinpif_eval2(y: f64) -> f64 {
256 let y2 = y * y;
257 f_estrin_polyeval7(
258 y2,
259 f64::from_bits(0x400921fb54442d18),
260 f64::from_bits(0xc014abbce625be09),
261 f64::from_bits(0x400466bc67754fff),
262 f64::from_bits(0xbfe32d2ccdfe9424),
263 f64::from_bits(0x3fb50782d5f14825),
264 f64::from_bits(0xbf7e2fe76fdffd2b),
265 f64::from_bits(0x3f3e357ef99eb0bb),
266 ) * y
267}
268
269/**
270Poly for cos(pi*y) on [-0.25; 0.25].
271Generated by Sollya:
272```text
273d = [0, 0.25];
274f_cos = cos(y*pi);
275Q = fpminimax(f_cos, [|0, 2, 4, 6, 8, 10|], [|1, D...|], d, relative, floating);
276```
277See ./notes/cospif_0p25.sollya
278**/
279#[inline]
280pub(crate) fn cospif_eval(y: f64) -> f64 {
281 let y2 = y * y;
282 f_estrin_polyeval7(
283 y2,
284 f64::from_bits(0x3ff0000000000000),
285 f64::from_bits(0xc013bd3cc9be459b),
286 f64::from_bits(0x40103c1f081b0c98),
287 f64::from_bits(0xbff55d3c7dc25308),
288 f64::from_bits(0x3fce1f4fb6e12563),
289 f64::from_bits(0xbf9a6c9c101c1182),
290 f64::from_bits(0x3f5f3d9faffda250),
291 )
292}