cinrs_rt/complex.rs
1//! C's complex arithmetic, as the generated code calls it.
2//!
3//! # What C's operators actually do
4//!
5//! Two rules decide everything in this module, and neither is what a naive
6//! reading of "the usual arithmetic conversions" suggests.
7//!
8//! * **A real operand stays real.** C99 6.3.1.8 says the common type of a real
9//! and a complex operand is the complex one, but the *result* it prescribes
10//! is the same one the componentwise operation gives — and GCC and Clang
11//! both compute it componentwise, which is observable: `3.0 * z` for
12//! `z = (-0.0, -0.0)` is `(-0.0, -0.0)` componentwise and `(+0.0, -0.0)`
13//! through the full product, because `3·(−0) − 0·(−0)` is `+0`. The one
14//! exception is `real / complex`, which really is the full division with the
15//! imaginary part of the numerator taken as zero. Each mixed form therefore
16//! has a function of its own here: [`mul_real_f64`], [`add_real_f64`],
17//! [`sub_real_f64`], [`real_sub_f64`], [`div_real_f64`] and
18//! [`real_div_f64`], with the `f32` twins beside them.
19//!
20//! * **Infinities are recovered.** C99 Annex G.5.1 (which GCC implements by
21//! default — `-fno-cx-limited-range`) says that a product or a quotient
22//! involving an infinity must be infinite, even where the naive formula
23//! produces NaN + iNaN out of an `∞ − ∞` or an `∞ · 0`. [`mul_f64`] and
24//! [`div_f64`] therefore compute the cheap answer first and only fix it up
25//! when both parts came out NaN, which is what makes the common case cost
26//! four multiplications and an addition.
27//!
28//! # The algorithms
29//!
30//! The product — in both widths — is the schoolbook one, `(ac − bd) +
31//! i(ad + bc)`, followed by Annex G.5.1's recovery: an infinite operand is
32//! reduced to a signed one or zero, a NaN in the *other* operand is reduced to
33//! a signed zero, and the product is recomputed scaled by infinity so that the
34//! sign of each part survives.
35//!
36//! The quotient takes a different route in each width, because what is cheap
37//! and exact in one is not available in the other.
38//!
39//! * [`div_f32`] uses the **closed form in the wider format**:
40//! `c² + d²` cannot overflow or underflow in `f64` for any `f32` operands, so
41//! `(ac + bd)/(c² + d²)` and `(bc − ad)/(c² + d²)` are computed in `f64` and
42//! rounded once.
43//! * [`div_f64`] has no wider format to hand, so it uses **Smith's
44//! algorithm** — divide through by whichever of the divisor's parts is
45//! larger, so that `c² + d²` cannot overflow when a single part can — with
46//! Baudin and Smith's refinement for a *subnormal* ratio, where `a · (d/c)`
47//! has already lost bits that the equal `(a/c) · d` still has.
48//!
49//! Both then apply the matching recovery: a zero divisor gives a signed
50//! infinity, an infinite dividend over a finite divisor gives an infinity, and
51//! a finite dividend over an infinite divisor gives a signed zero.
52//!
53//! All of it is written from the standard's description rather than taken from
54//! any implementation, and `tests/complex.rs` checks the result against the
55//! host's own C compiler: every combination of `0`, `−0`, `±1`, `±2`, `±10⁸`,
56//! `±10⁻⁸`, `±10¹⁵⁰`, `±10⁻¹⁵⁰`, `±∞` and NaN — a hundred and thirty thousand
57//! pairs — agrees exactly, and the product agrees for the `10³⁰⁰` and
58//! subnormal magnitudes too.
59//!
60//! # Where it is not the last word
61//!
62//! `libgcc` goes further on the `f64` quotient: it *prescales* both operands
63//! by powers of two before dividing, which buys accuracy in the last corner —
64//! operands whose exponents are hundreds apart, where an intermediate of
65//! Smith's algorithm underflows even after the refinement above. GCC's own
66//! `gcc.c-torture/execute/ieee/cdivchkd.c` is four such quotients, and two of
67//! its four are beyond what is here. C leaves the accuracy of complex
68//! arithmetic implementation-defined (Annex G.6), so this is a quality gap and
69//! not a conformance one; `doc/gcc-torture.md` records it.
70
71use crate::Complex;
72
73/// Generates the whole module for one floating format.
74///
75/// `$t` is the component type, `$suffix` the one the C library and this module
76/// spell the format with, and the two `$doc` fragments name it in the
77/// documentation.
78macro_rules! complex_ops {
79 ($t:ty, $mul:ident, $div:ident, $mul_real:ident, $real_mul:ident, $add_real:ident,
80 $real_add:ident, $sub_real:ident, $real_sub:ident, $div_real:ident, $real_div:ident,
81 $conj:ident, $proj:ident, $nonzero:ident, $eq:ident, $ne:ident, $cty:literal) => {
82 /// The product of two
83 #[doc = $cty]
84 /// values (C99 6.5.5, Annex G.5.1).
85 ///
86 /// The naive product, with the infinities Annex G.5.1 requires
87 /// recovered when it comes out NaN + iNaN.
88 #[inline]
89 pub fn $mul(z: Complex<$t>, w: Complex<$t>) -> Complex<$t> {
90 let (mut a, mut b, mut c, mut d) = (z.re, z.im, w.re, w.im);
91 let (ac, bd, ad, bc) = (a * c, b * d, a * d, b * c);
92 let mut x = ac - bd;
93 let mut y = ad + bc;
94 if x.is_nan() && y.is_nan() {
95 let mut recalc = false;
96 // An infinite operand: reduce it to a signed one or zero, and
97 // reduce a NaN in the other operand to a signed zero, so that
98 // the recomputation below cannot produce another NaN.
99 if a.is_infinite() || b.is_infinite() {
100 a = unit(a);
101 b = unit(b);
102 c = tame(c);
103 d = tame(d);
104 recalc = true;
105 }
106 if c.is_infinite() || d.is_infinite() {
107 c = unit(c);
108 d = unit(d);
109 a = tame(a);
110 b = tame(b);
111 recalc = true;
112 }
113 // Neither operand is infinite, but one of the four products
114 // overflowed to one: the result is infinite all the same.
115 if !recalc
116 && (ac.is_infinite()
117 || bd.is_infinite()
118 || ad.is_infinite()
119 || bc.is_infinite())
120 {
121 a = tame(a);
122 b = tame(b);
123 c = tame(c);
124 d = tame(d);
125 recalc = true;
126 }
127 if recalc {
128 let inf = <$t>::INFINITY;
129 x = inf * (a * c - b * d);
130 y = inf * (a * d + b * c);
131 }
132 }
133 Complex { re: x, im: y }
134 }
135
136 /// A
137 #[doc = $cty]
138 /// value times a real one, which C computes componentwise.
139 #[inline]
140 pub fn $mul_real(z: Complex<$t>, x: $t) -> Complex<$t> {
141 Complex {
142 re: z.re * x,
143 im: z.im * x,
144 }
145 }
146
147 /// A real value times a
148 #[doc = $cty]
149 /// one: the same product, with the operands in source order so that
150 /// the generated code evaluates them where they were written.
151 #[inline]
152 pub fn $real_mul(x: $t, z: Complex<$t>) -> Complex<$t> {
153 $mul_real(z, x)
154 }
155
156 /// A
157 #[doc = $cty]
158 /// value plus a real one, which touches the real part only.
159 #[inline]
160 pub fn $add_real(z: Complex<$t>, x: $t) -> Complex<$t> {
161 Complex {
162 re: z.re + x,
163 im: z.im,
164 }
165 }
166
167 /// A real value plus a
168 #[doc = $cty]
169 /// one; see [`
170 #[doc = stringify!($real_mul)]
171 /// `] for why the order matters.
172 #[inline]
173 pub fn $real_add(x: $t, z: Complex<$t>) -> Complex<$t> {
174 Complex {
175 re: x + z.re,
176 im: z.im,
177 }
178 }
179
180 /// A
181 #[doc = $cty]
182 /// value minus a real one, which touches the real part only.
183 #[inline]
184 pub fn $sub_real(z: Complex<$t>, x: $t) -> Complex<$t> {
185 Complex {
186 re: z.re - x,
187 im: z.im,
188 }
189 }
190
191 /// A real value minus a
192 #[doc = $cty]
193 /// one, which negates the imaginary part.
194 #[inline]
195 pub fn $real_sub(x: $t, z: Complex<$t>) -> Complex<$t> {
196 Complex {
197 re: x - z.re,
198 im: -z.im,
199 }
200 }
201
202 /// A
203 #[doc = $cty]
204 /// value divided by a real one, which C computes componentwise.
205 #[inline]
206 pub fn $div_real(z: Complex<$t>, x: $t) -> Complex<$t> {
207 Complex {
208 re: z.re / x,
209 im: z.im / x,
210 }
211 }
212
213 /// A real value divided by a
214 #[doc = $cty]
215 /// one.
216 ///
217 /// The one mixed form that is *not* componentwise: it is the full
218 /// complex division with a zero imaginary part in the numerator, which
219 /// is what GCC and Clang emit.
220 #[inline]
221 pub fn $real_div(x: $t, z: Complex<$t>) -> Complex<$t> {
222 $div(Complex { re: x, im: 0 as $t }, z)
223 }
224
225 /// The conjugate of a
226 #[doc = $cty]
227 /// value: `conj`, and GNU's `~z`.
228 #[inline]
229 pub fn $conj(z: Complex<$t>) -> Complex<$t> {
230 Complex {
231 re: z.re,
232 im: -z.im,
233 }
234 }
235
236 /// The projection of a
237 #[doc = $cty]
238 /// value onto the Riemann sphere: C99 7.3.9.5's `cproj`.
239 ///
240 /// Everything is itself except a value with an infinite part, which
241 /// becomes `+∞` with the imaginary part's sign kept on a zero.
242 #[inline]
243 pub fn $proj(z: Complex<$t>) -> Complex<$t> {
244 if z.re.is_infinite() || z.im.is_infinite() {
245 Complex {
246 re: <$t>::INFINITY,
247 im: copysign(0 as $t, z.im),
248 }
249 } else {
250 z
251 }
252 }
253
254 /// Whether a
255 #[doc = $cty]
256 /// value is non-zero, which is what C's conversion to `_Bool` and a
257 /// controlling expression ask (C99 6.3.1.2).
258 ///
259 /// Either part being non-zero is enough, so a NaN counts as true.
260 #[inline]
261 pub fn $nonzero(z: Complex<$t>) -> bool {
262 z.re != 0 as $t || z.im != 0 as $t
263 }
264
265 /// Whether two
266 #[doc = $cty]
267 /// values are equal: C99 6.5.9p3, both parts equal.
268 ///
269 /// The same answer `PartialEq` gives, and it is here because that one
270 /// takes `&self`: a reference to a field of a `#[repr(packed)]`
271 /// record is `E0793`, and a packed complex member is exactly what
272 /// `gcc.c-torture/execute/20020227-1` compares. Taking both operands
273 /// by value asks for a copy, which a packed field will give.
274 #[inline]
275 pub fn $eq(z: Complex<$t>, w: Complex<$t>) -> bool {
276 z.re == w.re && z.im == w.im
277 }
278
279 /// Whether two
280 #[doc = $cty]
281 /// values differ — the negation of
282 #[doc = concat!("[`", stringify!($eq), "`]")]
283 /// , and `!=` in C.
284 #[inline]
285 pub fn $ne(z: Complex<$t>, w: Complex<$t>) -> bool {
286 !$eq(z, w)
287 }
288 };
289}
290
291complex_ops!(
292 f32,
293 mul_f32,
294 div_f32,
295 mul_real_f32,
296 real_mul_f32,
297 add_real_f32,
298 real_add_f32,
299 sub_real_f32,
300 real_sub_f32,
301 div_real_f32,
302 real_div_f32,
303 conj_f32,
304 proj_f32,
305 nonzero_f32,
306 eq_f32,
307 ne_f32,
308 "`float _Complex`"
309);
310
311complex_ops!(
312 f64,
313 mul_f64,
314 div_f64,
315 mul_real_f64,
316 real_mul_f64,
317 add_real_f64,
318 real_add_f64,
319 sub_real_f64,
320 real_sub_f64,
321 div_real_f64,
322 real_div_f64,
323 conj_f64,
324 proj_f64,
325 nonzero_f64,
326 eq_f64,
327 ne_f64,
328 "`double _Complex`"
329);
330
331/// The quotient of two `float _Complex` values (C99 6.5.5, Annex G.5.1).
332///
333/// The closed form, computed in `f64`: `c² + d²` is exact enough and can
334/// neither overflow nor underflow there for any `f32` operands, so no scaling
335/// is needed and the result is rounded exactly once.
336#[inline]
337pub fn div_f32(z: Complex<f32>, w: Complex<f32>) -> Complex<f32> {
338 let (a, b) = (f64::from(z.re), f64::from(z.im));
339 let (c, d) = (f64::from(w.re), f64::from(w.im));
340 let denom = c * c + d * d;
341 let (x, y) = recover_quotient(a, b, c, d, (a * c + b * d) / denom, (b * c - a * d) / denom);
342 Complex {
343 re: x as f32,
344 im: y as f32,
345 }
346}
347
348/// The quotient of two `double _Complex` values (C99 6.5.5, Annex G.5.1).
349///
350/// Smith's algorithm: there is no wider format to compute `c² + d²` in, so the
351/// division is carried out through the ratio of the divisor's two parts, which
352/// keeps the denominator in range whenever the quotient itself is.
353///
354/// The `ratio.abs() > f64::MIN_POSITIVE` test is Smith's algorithm's one weak
355/// spot, closed the way Baudin and Smith describe: when the ratio is
356/// *subnormal* it has already lost bits, so multiplying by it throws away
357/// precision the operands still had. `a · (d/c)` and `(a/c) · d` are the same
358/// number, and the second one keeps it, so the second one is used exactly
359/// where the first would not do.
360#[inline]
361pub fn div_f64(z: Complex<f64>, w: Complex<f64>) -> Complex<f64> {
362 let (a, b, c, d) = (z.re, z.im, w.re, w.im);
363 let (x, y) = if abs(c) < abs(d) {
364 let ratio = c / d;
365 let denom = c * ratio + d;
366 if abs(ratio) > f64::MIN_POSITIVE {
367 ((a * ratio + b) / denom, (b * ratio - a) / denom)
368 } else {
369 (((a / d) * c + b) / denom, ((b / d) * c - a) / denom)
370 }
371 } else {
372 let ratio = d / c;
373 let denom = d * ratio + c;
374 if abs(ratio) > f64::MIN_POSITIVE {
375 ((b * ratio + a) / denom, (b - a * ratio) / denom)
376 } else {
377 (((b / c) * d + a) / denom, (b - (a / c) * d) / denom)
378 }
379 };
380 let (re, im) = recover_quotient(a, b, c, d, x, y);
381 Complex { re, im }
382}
383
384/// Annex G.5.1's recovery for a quotient that came out NaN + iNaN.
385///
386/// Shared by both widths: [`div_f32`] has already widened its operands, so the
387/// three cases — a zero divisor, an infinite dividend, an infinite divisor —
388/// are the same arithmetic in both.
389#[inline]
390fn recover_quotient(mut a: f64, mut b: f64, mut c: f64, mut d: f64, x: f64, y: f64) -> (f64, f64) {
391 if !(x.is_nan() && y.is_nan()) {
392 return (x, y);
393 }
394 let inf = f64::INFINITY;
395 if c == 0.0 && d == 0.0 && (!a.is_nan() || !b.is_nan()) {
396 // Division by zero: a signed infinity, not a NaN.
397 let scale = copysign(inf, c);
398 return (scale * a, scale * b);
399 }
400 if (a.is_infinite() || b.is_infinite()) && c.is_finite() && d.is_finite() {
401 a = unit(a);
402 b = unit(b);
403 return (inf * (a * c + b * d), inf * (b * c - a * d));
404 }
405 if (c.is_infinite() || d.is_infinite()) && a.is_finite() && b.is_finite() {
406 c = unit(c);
407 d = unit(d);
408 return (0.0 * (a * c + b * d), 0.0 * (b * c - a * d));
409 }
410 (x, y)
411}
412
413/// `float _Complex` widened to `double _Complex`.
414#[inline]
415pub fn widen_f32(z: Complex<f32>) -> Complex<f64> {
416 Complex {
417 re: f64::from(z.re),
418 im: f64::from(z.im),
419 }
420}
421
422/// `double _Complex` narrowed to `float _Complex`.
423#[inline]
424pub fn narrow_f64(z: Complex<f64>) -> Complex<f32> {
425 Complex {
426 re: z.re as f32,
427 im: z.im as f32,
428 }
429}
430
431/// The magnitude of a float, without `core::f32::abs` — which this crate does
432/// not need a `libm` for.
433trait Bits: Copy {
434 /// `|x|`.
435 fn magnitude(self) -> Self;
436 /// `x` with the sign of `y`.
437 fn with_sign(self, y: Self) -> Self;
438}
439
440macro_rules! bits {
441 ($t:ty, $u:ty) => {
442 impl Bits for $t {
443 #[inline]
444 fn magnitude(self) -> Self {
445 const SIGN: $u = 1 << (<$u>::BITS - 1);
446 <$t>::from_bits(self.to_bits() & !SIGN)
447 }
448 #[inline]
449 fn with_sign(self, y: Self) -> Self {
450 const SIGN: $u = 1 << (<$u>::BITS - 1);
451 <$t>::from_bits((self.to_bits() & !SIGN) | (y.to_bits() & SIGN))
452 }
453 }
454 };
455}
456
457bits!(f32, u32);
458bits!(f64, u64);
459
460/// `|x|`, on either width.
461#[inline]
462fn abs<T: Bits>(x: T) -> T {
463 x.magnitude()
464}
465
466/// `x` with the sign bit of `y`, on either width.
467#[inline]
468fn copysign<T: Bits>(x: T, y: T) -> T {
469 x.with_sign(y)
470}
471
472/// One or zero, with `x`'s sign: what Annex G.5.1 reduces an operand to before
473/// recomputing an overflowed product or quotient.
474#[inline]
475fn unit<T: Bits + Float>(x: T) -> T {
476 copysign(if x.is_infinite() { T::ONE } else { T::ZERO }, x)
477}
478
479/// A NaN reduced to a signed zero, and everything else left alone: the other
480/// half of Annex G.5.1's recovery.
481#[inline]
482fn tame<T: Bits + Float>(x: T) -> T {
483 if x.is_nan() { copysign(T::ZERO, x) } else { x }
484}
485
486/// The handful of predicates [`unit`] and [`tame`] need on both widths.
487trait Float: Copy {
488 /// `0`.
489 const ZERO: Self;
490 /// `1`.
491 const ONE: Self;
492 /// Whether this is an infinity.
493 fn is_infinite(self) -> bool;
494 /// Whether this is a NaN.
495 fn is_nan(self) -> bool;
496}
497
498macro_rules! float {
499 ($t:ty) => {
500 impl Float for $t {
501 const ZERO: Self = 0.0;
502 const ONE: Self = 1.0;
503 #[inline]
504 fn is_infinite(self) -> bool {
505 <$t>::is_infinite(self)
506 }
507 #[inline]
508 fn is_nan(self) -> bool {
509 <$t>::is_nan(self)
510 }
511 }
512 };
513}
514
515float!(f32);
516float!(f64);
517
518#[cfg(test)]
519mod tests {
520 use super::*;
521
522 fn same(a: f64, b: f64) -> bool {
523 if a.is_nan() && b.is_nan() {
524 return true;
525 }
526 a == b && a.is_sign_negative() == b.is_sign_negative()
527 }
528
529 fn same_c(a: Complex<f64>, b: Complex<f64>) -> bool {
530 same(a.re, b.re) && same(a.im, b.im)
531 }
532
533 #[test]
534 fn the_ordinary_product_and_quotient_are_the_school_ones() {
535 let z = Complex::new(1.0, 2.0);
536 let w = Complex::new(3.0, -4.0);
537 assert_eq!(mul_f64(z, w), Complex::new(11.0, 2.0));
538 assert_eq!(div_f64(z, w), Complex::new(-0.2, 0.4));
539 }
540
541 #[test]
542 fn an_infinity_survives_a_naive_nan() {
543 let inf = f64::INFINITY;
544 // (∞, 0) · (3, −4) is (∞·3 − 0·(−4), ∞·(−4) + 0·3) = (∞, −∞) only
545 // after the recovery: the naive imaginary part is −∞ + NaN.
546 assert!(same_c(
547 mul_f64(Complex::new(inf, 0.0), Complex::new(3.0, -4.0)),
548 Complex::new(inf, -inf)
549 ));
550 assert!(same_c(
551 div_f64(Complex::new(inf, 0.0), Complex::new(3.0, -4.0)),
552 Complex::new(inf, inf)
553 ));
554 // A finite value over an infinite one is a signed zero.
555 assert!(same_c(
556 div_f64(Complex::new(3.0, -4.0), Complex::new(inf, 0.0)),
557 Complex::new(0.0, -0.0)
558 ));
559 }
560
561 #[test]
562 fn division_by_zero_is_a_signed_infinity() {
563 let inf = f64::INFINITY;
564 assert!(same_c(
565 div_f64(Complex::new(1.0, 2.0), Complex::new(0.0, 0.0)),
566 Complex::new(inf, inf)
567 ));
568 assert!(same_c(
569 div_f64(Complex::new(1.0, 2.0), Complex::new(-0.0, -0.0)),
570 Complex::new(-inf, -inf)
571 ));
572 }
573
574 #[test]
575 fn a_real_operand_is_componentwise() {
576 let nzero = Complex::new(-0.0, -0.0);
577 // The full product would give (+0, −0) here; C gives (−0, −0).
578 assert!(same_c(mul_real_f64(nzero, 3.0), Complex::new(-0.0, -0.0)));
579 assert!(same_c(
580 real_sub_f64(0.0, Complex::new(1.0, 0.0)),
581 Complex::new(-1.0, -0.0)
582 ));
583 assert!(same_c(
584 div_real_f64(Complex::new(1.0, 2.0), 0.0),
585 Complex::new(f64::INFINITY, f64::INFINITY)
586 ));
587 }
588
589 #[test]
590 fn conjugation_projection_and_truth() {
591 let z = Complex::new(1.0, 2.0);
592 assert_eq!(conj_f64(z), Complex::new(1.0, -2.0));
593 assert_eq!(proj_f64(z), z);
594 assert!(same_c(
595 proj_f64(Complex::new(1.0, f64::NEG_INFINITY)),
596 Complex::new(f64::INFINITY, -0.0)
597 ));
598 assert!(nonzero_f64(Complex::new(0.0, 1.0)));
599 assert!(!nonzero_f64(Complex::new(0.0, -0.0)));
600 assert!(nonzero_f64(Complex::new(f64::NAN, 0.0)));
601 }
602
603 #[test]
604 fn the_f32_forms_agree_with_the_f64_ones_on_exact_values() {
605 let z = Complex::new(1.0f32, 2.0);
606 let w = Complex::new(3.0f32, -4.0);
607 assert_eq!(mul_f32(z, w), Complex::new(11.0, 2.0));
608 assert_eq!(widen_f32(mul_f32(z, w)), Complex::new(11.0f64, 2.0));
609 assert_eq!(
610 narrow_f64(Complex::new(11.0f64, 2.0)),
611 Complex::new(11.0f32, 2.0)
612 );
613 }
614}