Skip to main content

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