1// origin: FreeBSD /usr/src/lib/msun/src/e_rem_pio2.c
2//
3// ====================================================
4// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
5//
6// Developed at SunPro, a Sun Microsystems, Inc. business.
7// Permission to use, copy, modify, and distribute this
8// software is freely granted, provided that this notice
9// is preserved.
10// ====================================================
11//
12// Optimized by Bruce D. Evans. */
13use super::rem_pio2_large;
14
15// #if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
16// #define EPS DBL_EPSILON
17const EPS: f64 = 2.2204460492503131e-16;
18// #elif FLT_EVAL_METHOD==2
19// #define EPS LDBL_EPSILON
20// #endif
21
22// TODO: Support FLT_EVAL_METHOD?
23
24const TO_INT: f64 = 1.5 / EPS;
25/// 53 bits of 2/pi
26const INV_PIO2: f64 = 6.36619772367581382433e-01; /* 0x3FE45F30, 0x6DC9C883 */
27/// first 33 bits of pi/2
28const PIO2_1: f64 = 1.57079632673412561417e+00; /* 0x3FF921FB, 0x54400000 */
29/// pi/2 - PIO2_1
30const PIO2_1T: f64 = 6.07710050650619224932e-11; /* 0x3DD0B461, 0x1A626331 */
31/// second 33 bits of pi/2
32const PIO2_2: f64 = 6.07710050630396597660e-11; /* 0x3DD0B461, 0x1A600000 */
33/// pi/2 - (PIO2_1+PIO2_2)
34const PIO2_2T: f64 = 2.02226624879595063154e-21; /* 0x3BA3198A, 0x2E037073 */
35/// third 33 bits of pi/2
36const PIO2_3: f64 = 2.02226624871116645580e-21; /* 0x3BA3198A, 0x2E000000 */
37/// pi/2 - (PIO2_1+PIO2_2+PIO2_3)
38const PIO2_3T: f64 = 8.47842766036889956997e-32; /* 0x397B839A, 0x252049C1 */
39
40// return the remainder of x rem pi/2 in y[0]+y[1]
41// use rem_pio2_large() for large x
42//
43// caller must handle the case when reduction is not needed: |x| ~<= pi/4 */
44#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
45pub(crate) fn rem_pio2(x: f64) -> (i32, f64, f64) {
46 let x1p24 = f64::from_bits(0x4170000000000000);
47
48 let sign = (f64::to_bits(x) >> 63) as i32;
49 let ix = (f64::to_bits(x) >> 32) as u32 & 0x7fffffff;
50
51 fn medium(x: f64, ix: u32) -> (i32, f64, f64) {
52 /* rint(x/(pi/2)), Assume round-to-nearest. */
53 let tmp = x as f64 * INV_PIO2 + TO_INT;
54 // force rounding of tmp to it's storage format on x87 to avoid
55 // excess precision issues.
56 #[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
57 let tmp = force_eval!(tmp);
58 let f_n = tmp - TO_INT;
59 let n = f_n as i32;
60 let mut r = x - f_n * PIO2_1;
61 let mut w = f_n * PIO2_1T; /* 1st round, good to 85 bits */
62 let mut y0 = r - w;
63 let ui = f64::to_bits(y0);
64 let ey = (ui >> 52) as i32 & 0x7ff;
65 let ex = (ix >> 20) as i32;
66 if ex - ey > 16 {
67 /* 2nd round, good to 118 bits */
68 let t = r;
69 w = f_n * PIO2_2;
70 r = t - w;
71 w = f_n * PIO2_2T - ((t - r) - w);
72 y0 = r - w;
73 let ey = (f64::to_bits(y0) >> 52) as i32 & 0x7ff;
74 if ex - ey > 49 {
75 /* 3rd round, good to 151 bits, covers all cases */
76 let t = r;
77 w = f_n * PIO2_3;
78 r = t - w;
79 w = f_n * PIO2_3T - ((t - r) - w);
80 y0 = r - w;
81 }
82 }
83 let y1 = (r - y0) - w;
84 (n, y0, y1)
85 }
86
87 if ix <= 0x400f6a7a {
88 /* |x| ~<= 5pi/4 */
89 if (ix & 0xfffff) == 0x921fb {
90 /* |x| ~= pi/2 or 2pi/2 */
91 return medium(x, ix); /* cancellation -- use medium case */
92 }
93 if ix <= 0x4002d97c {
94 /* |x| ~<= 3pi/4 */
95 if sign == 0 {
96 let z = x - PIO2_1; /* one round good to 85 bits */
97 let y0 = z - PIO2_1T;
98 let y1 = (z - y0) - PIO2_1T;
99 return (1, y0, y1);
100 } else {
101 let z = x + PIO2_1;
102 let y0 = z + PIO2_1T;
103 let y1 = (z - y0) + PIO2_1T;
104 return (-1, y0, y1);
105 }
106 } else if sign == 0 {
107 let z = x - 2.0 * PIO2_1;
108 let y0 = z - 2.0 * PIO2_1T;
109 let y1 = (z - y0) - 2.0 * PIO2_1T;
110 return (2, y0, y1);
111 } else {
112 let z = x + 2.0 * PIO2_1;
113 let y0 = z + 2.0 * PIO2_1T;
114 let y1 = (z - y0) + 2.0 * PIO2_1T;
115 return (-2, y0, y1);
116 }
117 }
118 if ix <= 0x401c463b {
119 /* |x| ~<= 9pi/4 */
120 if ix <= 0x4015fdbc {
121 /* |x| ~<= 7pi/4 */
122 if ix == 0x4012d97c {
123 /* |x| ~= 3pi/2 */
124 return medium(x, ix);
125 }
126 if sign == 0 {
127 let z = x - 3.0 * PIO2_1;
128 let y0 = z - 3.0 * PIO2_1T;
129 let y1 = (z - y0) - 3.0 * PIO2_1T;
130 return (3, y0, y1);
131 } else {
132 let z = x + 3.0 * PIO2_1;
133 let y0 = z + 3.0 * PIO2_1T;
134 let y1 = (z - y0) + 3.0 * PIO2_1T;
135 return (-3, y0, y1);
136 }
137 } else {
138 if ix == 0x401921fb {
139 /* |x| ~= 4pi/2 */
140 return medium(x, ix);
141 }
142 if sign == 0 {
143 let z = x - 4.0 * PIO2_1;
144 let y0 = z - 4.0 * PIO2_1T;
145 let y1 = (z - y0) - 4.0 * PIO2_1T;
146 return (4, y0, y1);
147 } else {
148 let z = x + 4.0 * PIO2_1;
149 let y0 = z + 4.0 * PIO2_1T;
150 let y1 = (z - y0) + 4.0 * PIO2_1T;
151 return (-4, y0, y1);
152 }
153 }
154 }
155 if ix < 0x413921fb {
156 /* |x| ~< 2^20*(pi/2), medium size */
157 return medium(x, ix);
158 }
159 /*
160 * all other (large) arguments
161 */
162 if ix >= 0x7ff00000 {
163 /* x is inf or NaN */
164 let y0 = x - x;
165 let y1 = y0;
166 return (0, y0, y1);
167 }
168 /* set z = scalbn(|x|,-ilogb(x)+23) */
169 let mut ui = f64::to_bits(x);
170 ui &= (!1) >> 12;
171 ui |= (0x3ff + 23) << 52;
172 let mut z = f64::from_bits(ui);
173 let mut tx = [0.0; 3];
174 for i in 0..2 {
175 i!(tx,i, =, z as i32 as f64);
176 z = (z - i!(tx, i)) * x1p24;
177 }
178 i!(tx,2, =, z);
179 /* skip zero terms, first term is non-zero */
180 let mut i = 2;
181 while i != 0 && i!(tx, i) == 0.0 {
182 i -= 1;
183 }
184 let mut ty = [0.0; 3];
185 let n = rem_pio2_large(&tx[..=i], &mut ty, ((ix as i32) >> 20) - (0x3ff + 23), 1);
186 if sign != 0 {
187 return (-n, -i!(ty, 0), -i!(ty, 1));
188 }
189 (n, i!(ty, 0), i!(ty, 1))
190}
191
192#[cfg(test)]
193mod tests {
194 use super::rem_pio2;
195
196 #[test]
197 fn test_near_pi() {
198 let arg = 3.141592025756836;
199 let arg = force_eval!(arg);
200 assert_eq!(
201 rem_pio2(arg),
202 (2, -6.278329573009626e-7, -2.1125998133974653e-23)
203 );
204 let arg = 3.141592033207416;
205 let arg = force_eval!(arg);
206 assert_eq!(
207 rem_pio2(arg),
208 (2, -6.20382377148128e-7, -2.1125998133974653e-23)
209 );
210 let arg = 3.141592144966125;
211 let arg = force_eval!(arg);
212 assert_eq!(
213 rem_pio2(arg),
214 (2, -5.086236681942706e-7, -2.1125998133974653e-23)
215 );
216 let arg = 3.141592979431152;
217 let arg = force_eval!(arg);
218 assert_eq!(
219 rem_pio2(arg),
220 (2, 3.2584135866119817e-7, -2.1125998133974653e-23)
221 );
222 }
223
224 #[test]
225 fn test_overflow_b9b847() {
226 let _ = rem_pio2(-3054214.5490637687);
227 }
228
229 #[test]
230 fn test_overflow_4747b9() {
231 let _ = rem_pio2(917340800458.2274);
232 }
233}
234