1#![allow(clippy::indexing_slicing)]
2#![allow(clippy::excessive_precision)]
3#![allow(clippy::approx_constant)]
4#![allow(clippy::eq_op)]
5
6use super::atan;
7use crate::Real;
8
9const PI: Real = 3.1415926535897931160E+00; const PI_LO: Real = 1.2246467991473531772E-16; pub const fn atan2(y: Real, x: Real) -> Real {
55 if x.is_nan() || y.is_nan() {
56 return x + y;
57 }
58
59 let mut ix = (Real::to_bits(x) >> 32) as u32;
60 let lx = Real::to_bits(x) as u32;
61 let mut iy = (Real::to_bits(y) >> 32) as u32;
62 let ly = Real::to_bits(y) as u32;
63
64 if ((ix.wrapping_sub(0x3ff00000)) | lx) == 0 {
66 return atan(y);
67 }
68
69 let m = ((iy >> 31) & 1) | ((ix >> 30) & 2); ix &= 0x7fffffff;
71 iy &= 0x7fffffff;
72
73 if (iy | ly) == 0 {
75 return match m {
76 0 | 1 => y, 2 => PI, _ => -PI, };
80 }
81
82 if (ix | lx) == 0 {
84 return if m & 1 != 0 { -PI / 2.0 } else { PI / 2.0 };
85 }
86
87 if ix == 0x7ff00000 {
89 if iy == 0x7ff00000 {
90 return match m {
91 0 => PI / 4.0, 1 => -PI / 4.0, 2 => 3.0 * PI / 4.0, _ => -3.0 * PI / 4.0, };
96 } else {
97 return match m {
98 0 => 0.0, 1 => -0.0, 2 => PI, _ => -PI, };
103 }
104 }
105
106 if ix.wrapping_add(64 << 20) < iy || iy == 0x7ff00000 {
108 return if m & 1 != 0 { -PI / 2.0 } else { PI / 2.0 };
109 }
110
111 let z = if (m & 2 != 0) && iy.wrapping_add(64 << 20) < ix {
113 0.0
115 } else {
116 atan(y.abs() / x.abs())
117 };
118
119 match m {
120 0 => z, 1 => -z, 2 => PI - (z - PI_LO), _ => (z - PI_LO) - PI, }
125}
126
127#[cfg(all(test, feature = "std"))]
128mod atan2_tests {
129 use super::atan2;
130 use crate::Real;
131
132 const PI: Real = 3.1415926535897931160E+00; const PI_LO: Real = 1.2246467991473531772E-16; const PI_2: Real = 1.5707963267948965580E+00; const PI_4: Real = 0.78539816339744830962E+00; const THREE_PI_4: Real = 2.3561944901923449288E+00; fn assert_close(a: Real, b: Real, msg: &str) {
141 if a.is_nan() && b.is_nan() {
142 return;
143 }
144 let diff = (a - b).abs();
145 if diff == 0.0 {
146 return;
147 }
148
149 let ulps = if a == 0.0 || b == 0.0 {
150 diff.to_bits() as i64
151 } else {
152 (a.to_bits() as i64).wrapping_sub(b.to_bits() as i64).abs()
153 };
154
155 assert!(
156 ulps <= 1 || diff / b.abs() < 1e-15,
157 "{msg}\n expected: {b:.20e}\n got: {a:.20e}\n ulps: {ulps}"
158 );
159 }
160
161 #[test]
162 fn sanity_check() {
163 let cases = [
164 (1.0, 1.0, PI_4),
165 (Real::sqrt(3.0), 1.0, PI / 3.0),
166 (1.0, Real::sqrt(3.0), PI / 6.0),
167 (0.0, 1.0, 0.0),
168 (1.0, 0.0, PI_2),
169 (-1.0, 1.0, -PI_4),
170 (-Real::sqrt(3.0), 1.0, -PI / 3.0),
171 (-1.0, Real::sqrt(3.0), -PI / 6.0),
172 (0.0, -1.0, PI),
173 (1.0, -1.0, THREE_PI_4),
174 (Real::sqrt(3.0), -1.0, 2.0 * PI / 3.0),
175 (-1.0, -1.0, -THREE_PI_4),
176 (-Real::sqrt(3.0), -1.0, -2.0 * PI / 3.0),
177 (-1.0, 0.0, -PI_2),
178 ];
179
180 for (y, x, expected) in cases.iter() {
181 assert_close(atan2(*y, *x), *expected, &format!("atan2({y}, {x})"));
182 }
183 }
184
185 #[test]
186 fn special_values() {
187 assert!(atan2(Real::NAN, 1.0).is_nan());
189 assert!(atan2(1.0, Real::NAN).is_nan());
190 assert!(atan2(Real::NAN, Real::NAN).is_nan());
191
192 assert_eq!(atan2(0.0, 1.0), 0.0);
194 assert_eq!(atan2(-0.0, 1.0), -0.0);
195 assert_eq!(atan2(0.0, -1.0), PI);
196 assert_eq!(atan2(-0.0, -1.0), -PI);
197
198 assert_eq!(atan2(1.0, 0.0), PI_2);
200 assert_eq!(atan2(-1.0, 0.0), -PI_2);
201
202 assert_eq!(atan2(1.0, Real::INFINITY), 0.0);
204 assert_eq!(atan2(-1.0, Real::INFINITY), -0.0);
205 assert_eq!(atan2(1.0, Real::NEG_INFINITY), PI);
206 assert_eq!(atan2(-1.0, Real::NEG_INFINITY), -PI);
207
208 assert_eq!(atan2(Real::INFINITY, 1.0), PI_2);
209 assert_eq!(atan2(Real::NEG_INFINITY, 1.0), -PI_2);
210
211 assert_eq!(atan2(Real::INFINITY, Real::INFINITY), PI_4);
212 assert_eq!(atan2(Real::NEG_INFINITY, Real::INFINITY), -PI_4);
213 assert_eq!(atan2(Real::INFINITY, Real::NEG_INFINITY), THREE_PI_4);
214 assert_eq!(atan2(Real::NEG_INFINITY, Real::NEG_INFINITY), -THREE_PI_4);
215 }
216
217 #[test]
218 fn extreme_ratio_cases() {
219 let huge = Real::from_bits(0x7fe0_0000_0000_0000);
221 assert_eq!(atan2(huge, 1.0), PI_2);
222 assert_eq!(atan2(-huge, 1.0), -PI_2);
223 assert_eq!(atan2(huge, -1.0), PI_2);
224 assert_eq!(atan2(-huge, -1.0), -PI_2);
225
226 let tiny = Real::from_bits(0x0010_0000_0000_0000);
228 assert_eq!(atan2(tiny, -1.0), PI); assert_eq!(atan2(-tiny, -1.0), -PI);
230 assert_eq!(atan2(tiny, 1.0), tiny); }
232
233 #[test]
234 fn pi_lo_correction() {
235 let y = 10.0;
238 let x = -1.0;
239 let result = atan2(y, x);
240
241 let z = super::atan(y.abs() / x.abs());
242 let expected = PI - (z - PI_LO); assert_close(result, expected, "PI_LO correction (x < 0, y > 0)");
245 }
246
247 #[test]
248 fn fast_path_x_equals_one() {
249 let y = 0.5;
250 assert_close(atan2(y, 1.0), super::atan(y), "fast-path x == 1.0");
251 }
252}