Skip to main content

cranelift_codegen/ir/
immediates.rs

1//! Immediate operands for Cranelift instructions
2//!
3//! This module defines the types of immediate operands that can appear on Cranelift instructions.
4//! Each type here should have a corresponding definition in the
5//! `cranelift-codegen/meta/src/shared/immediates` crate in the meta language.
6
7use alloc::vec::Vec;
8use core::cmp::Ordering;
9use core::fmt::{self, Display, Formatter};
10use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Sub};
11use core::str::FromStr;
12use libm::Libm;
13#[cfg(feature = "enable-serde")]
14use serde_derive::{Deserialize, Serialize};
15
16/// Convert a type into a vector of bytes; all implementors in this file must use little-endian
17/// orderings of bytes to match WebAssembly's little-endianness.
18pub trait IntoBytes {
19    /// Return the little-endian byte representation of the implementing type.
20    fn into_bytes(self) -> Vec<u8>;
21}
22
23impl IntoBytes for u8 {
24    fn into_bytes(self) -> Vec<u8> {
25        vec![self]
26    }
27}
28
29impl IntoBytes for i8 {
30    fn into_bytes(self) -> Vec<u8> {
31        vec![self as u8]
32    }
33}
34
35impl IntoBytes for i16 {
36    fn into_bytes(self) -> Vec<u8> {
37        self.to_le_bytes().to_vec()
38    }
39}
40
41impl IntoBytes for i32 {
42    fn into_bytes(self) -> Vec<u8> {
43        self.to_le_bytes().to_vec()
44    }
45}
46
47impl IntoBytes for Vec<u8> {
48    fn into_bytes(self) -> Vec<u8> {
49        self
50    }
51}
52
53/// 64-bit immediate signed integer operand.
54///
55/// An `Imm64` operand can also be used to represent immediate values of smaller integer types by
56/// sign-extending to `i64`.
57#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
58#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
59pub struct Imm64(i64);
60
61impl Imm64 {
62    /// Create a new `Imm64` representing the signed number `x`.
63    pub fn new(x: i64) -> Self {
64        Self(x)
65    }
66
67    /// Return self negated.
68    pub fn wrapping_neg(self) -> Self {
69        Self(self.0.wrapping_neg())
70    }
71
72    /// Returns the value of this immediate.
73    pub fn bits(&self) -> i64 {
74        self.0
75    }
76
77    /// Mask this immediate to the given power-of-two bit width.
78    #[must_use]
79    pub(crate) fn mask_to_width(&self, bit_width: u32) -> Self {
80        debug_assert!(bit_width.is_power_of_two());
81
82        if bit_width >= 64 {
83            return *self;
84        }
85
86        let bit_width = i64::from(bit_width);
87        let mask = (1 << bit_width) - 1;
88        let masked = self.0 & mask;
89        Imm64(masked)
90    }
91
92    /// Sign extend this immediate as if it were a signed integer of the given
93    /// power-of-two width.
94    #[must_use]
95    pub fn sign_extend_from_width(&self, bit_width: u32) -> Self {
96        debug_assert!(
97            bit_width.is_power_of_two(),
98            "{bit_width} is not a power of two"
99        );
100
101        if bit_width >= 64 {
102            return *self;
103        }
104
105        let bit_width = i64::from(bit_width);
106        let delta = 64 - bit_width;
107        let sign_extended = (self.0 << delta) >> delta;
108        Imm64(sign_extended)
109    }
110
111    /// Zero extend this immediate as if it were an unsigned integer of the
112    /// given power-of-two width.
113    #[must_use]
114    pub fn zero_extend_from_width(&self, bit_width: u32) -> Self {
115        debug_assert!(
116            bit_width.is_power_of_two(),
117            "{bit_width} is not a power of two"
118        );
119
120        if bit_width >= 64 {
121            return *self;
122        }
123
124        let bit_width = u64::from(bit_width);
125        let delta = 64 - bit_width;
126        let zero_extended = (self.0.cast_unsigned() << delta) >> delta;
127        Imm64(zero_extended.cast_signed())
128    }
129}
130
131impl From<Imm64> for i64 {
132    fn from(val: Imm64) -> i64 {
133        val.0
134    }
135}
136
137impl IntoBytes for Imm64 {
138    fn into_bytes(self) -> Vec<u8> {
139        self.0.to_le_bytes().to_vec()
140    }
141}
142
143impl From<i64> for Imm64 {
144    fn from(x: i64) -> Self {
145        Self(x)
146    }
147}
148
149impl Display for Imm64 {
150    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
151        let x = self.0;
152        if x < 10_000 {
153            // Use decimal for small and negative numbers.
154            write!(f, "{x}")
155        } else {
156            write_hex(x as u64, f)
157        }
158    }
159}
160
161/// Parse a 64-bit signed number.
162fn parse_i64(s: &str) -> Result<i64, &'static str> {
163    let negative = s.starts_with('-');
164    let s2 = if negative || s.starts_with('+') {
165        &s[1..]
166    } else {
167        s
168    };
169
170    let mut value = parse_u64(s2)?;
171
172    // We support the range-and-a-half from -2^63 .. 2^64-1.
173    if negative {
174        value = value.wrapping_neg();
175        // Don't allow large negative values to wrap around and become positive.
176        if value as i64 > 0 {
177            return Err("Negative number too small");
178        }
179    }
180    Ok(value as i64)
181}
182
183impl FromStr for Imm64 {
184    type Err = &'static str;
185
186    // Parse a decimal or hexadecimal `Imm64`, formatted as above.
187    fn from_str(s: &str) -> Result<Self, &'static str> {
188        parse_i64(s).map(Self::new)
189    }
190}
191
192/// 64-bit immediate unsigned integer operand.
193///
194/// A `Uimm64` operand can also be used to represent immediate values of smaller integer types by
195/// zero-extending to `i64`.
196#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
197#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
198pub struct Uimm64(u64);
199
200impl Uimm64 {
201    /// Create a new `Uimm64` representing the unsigned number `x`.
202    pub fn new(x: u64) -> Self {
203        Self(x)
204    }
205
206    /// Return self negated.
207    pub fn wrapping_neg(self) -> Self {
208        Self(self.0.wrapping_neg())
209    }
210}
211
212impl From<Uimm64> for u64 {
213    fn from(val: Uimm64) -> u64 {
214        val.0
215    }
216}
217
218impl From<u64> for Uimm64 {
219    fn from(x: u64) -> Self {
220        Self(x)
221    }
222}
223
224/// Hexadecimal with a multiple of 4 digits and group separators:
225///
226///   0xfff0
227///   0x0001_ffff
228///   0xffff_ffff_fff8_4400
229///
230fn write_hex(x: u64, f: &mut Formatter) -> fmt::Result {
231    let mut pos = (64 - x.leading_zeros() - 1) & 0xf0;
232    write!(f, "0x{:04x}", (x >> pos) & 0xffff)?;
233    while pos > 0 {
234        pos -= 16;
235        write!(f, "_{:04x}", (x >> pos) & 0xffff)?;
236    }
237    Ok(())
238}
239
240impl Display for Uimm64 {
241    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
242        let x = self.0;
243        if x < 10_000 {
244            // Use decimal for small numbers.
245            write!(f, "{x}")
246        } else {
247            write_hex(x, f)
248        }
249    }
250}
251
252/// Parse a 64-bit unsigned number.
253fn parse_u64(s: &str) -> Result<u64, &'static str> {
254    let mut value: u64 = 0;
255    let mut digits = 0;
256
257    if s.starts_with("-0x") {
258        return Err("Invalid character in hexadecimal number");
259    } else if let Some(num) = s.strip_prefix("0x") {
260        // Hexadecimal.
261        for ch in num.chars() {
262            match ch.to_digit(16) {
263                Some(digit) => {
264                    digits += 1;
265                    if digits > 16 {
266                        return Err("Too many hexadecimal digits");
267                    }
268                    // This can't overflow given the digit limit.
269                    value = (value << 4) | u64::from(digit);
270                }
271                None => {
272                    // Allow embedded underscores, but fail on anything else.
273                    if ch != '_' {
274                        return Err("Invalid character in hexadecimal number");
275                    }
276                }
277            }
278        }
279    } else {
280        // Decimal number, possibly negative.
281        for ch in s.chars() {
282            match ch.to_digit(10) {
283                Some(digit) => {
284                    digits += 1;
285                    match value.checked_mul(10) {
286                        None => return Err("Too large decimal number"),
287                        Some(v) => value = v,
288                    }
289                    match value.checked_add(u64::from(digit)) {
290                        None => return Err("Too large decimal number"),
291                        Some(v) => value = v,
292                    }
293                }
294                None => {
295                    // Allow embedded underscores, but fail on anything else.
296                    if ch != '_' {
297                        return Err("Invalid character in decimal number");
298                    }
299                }
300            }
301        }
302    }
303
304    if digits == 0 {
305        return Err("No digits in number");
306    }
307
308    Ok(value)
309}
310
311impl FromStr for Uimm64 {
312    type Err = &'static str;
313
314    // Parse a decimal or hexadecimal `Uimm64`, formatted as above.
315    fn from_str(s: &str) -> Result<Self, &'static str> {
316        parse_u64(s).map(Self::new)
317    }
318}
319
320/// 8-bit unsigned integer immediate operand.
321///
322/// This is used to indicate lane indexes typically.
323pub type Uimm8 = u8;
324
325/// A 32-bit unsigned integer immediate operand.
326///
327/// This is used to represent sizes of memory objects.
328#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
329#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
330pub struct Uimm32(u32);
331
332impl From<Uimm32> for u32 {
333    fn from(val: Uimm32) -> u32 {
334        val.0
335    }
336}
337
338impl From<Uimm32> for u64 {
339    fn from(val: Uimm32) -> u64 {
340        val.0.into()
341    }
342}
343
344impl From<Uimm32> for i64 {
345    fn from(val: Uimm32) -> i64 {
346        i64::from(val.0)
347    }
348}
349
350impl From<u32> for Uimm32 {
351    fn from(x: u32) -> Self {
352        Self(x)
353    }
354}
355
356impl Display for Uimm32 {
357    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
358        if self.0 < 10_000 {
359            write!(f, "{}", self.0)
360        } else {
361            write_hex(u64::from(self.0), f)
362        }
363    }
364}
365
366impl FromStr for Uimm32 {
367    type Err = &'static str;
368
369    // Parse a decimal or hexadecimal `Uimm32`, formatted as above.
370    fn from_str(s: &str) -> Result<Self, &'static str> {
371        parse_i64(s).and_then(|x| {
372            if 0 <= x && x <= i64::from(u32::MAX) {
373                Ok(Self(x as u32))
374            } else {
375                Err("Uimm32 out of range")
376            }
377        })
378    }
379}
380
381/// A 128-bit immediate operand.
382///
383/// This is used as an immediate value in SIMD instructions.
384#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
385#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
386pub struct V128Imm(pub [u8; 16]);
387
388impl V128Imm {
389    /// Iterate over the bytes in the constant.
390    pub fn bytes(&self) -> impl Iterator<Item = &u8> {
391        self.0.iter()
392    }
393
394    /// Convert the immediate into a vector.
395    pub fn to_vec(self) -> Vec<u8> {
396        self.0.to_vec()
397    }
398
399    /// Convert the immediate into a slice.
400    pub fn as_slice(&self) -> &[u8] {
401        &self.0[..]
402    }
403}
404
405impl From<&[u8]> for V128Imm {
406    fn from(slice: &[u8]) -> Self {
407        assert_eq!(slice.len(), 16);
408        let mut buffer = [0; 16];
409        buffer.copy_from_slice(slice);
410        Self(buffer)
411    }
412}
413
414impl From<u128> for V128Imm {
415    fn from(val: u128) -> Self {
416        V128Imm(val.to_le_bytes())
417    }
418}
419
420/// 32-bit signed immediate offset.
421///
422/// This is used to encode an immediate offset for load/store instructions. All supported ISAs have
423/// a maximum load/store offset that fits in an `i32`.
424#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
425#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
426pub struct Offset32(i32);
427
428impl Offset32 {
429    /// Create a new `Offset32` representing the signed number `x`.
430    pub fn new(x: i32) -> Self {
431        Self(x)
432    }
433
434    /// Create a new `Offset32` representing the signed number `x` if possible.
435    pub fn try_from_i64(x: i64) -> Option<Self> {
436        let x = i32::try_from(x).ok()?;
437        Some(Self::new(x))
438    }
439
440    /// Add in the signed number `x` if possible.
441    pub fn try_add_i64(self, x: i64) -> Option<Self> {
442        let x = i32::try_from(x).ok()?;
443        let ret = self.0.checked_add(x)?;
444        Some(Self::new(ret))
445    }
446}
447
448impl From<Offset32> for i32 {
449    fn from(val: Offset32) -> i32 {
450        val.0
451    }
452}
453
454impl From<Offset32> for i64 {
455    fn from(val: Offset32) -> i64 {
456        i64::from(val.0)
457    }
458}
459
460impl From<i32> for Offset32 {
461    fn from(x: i32) -> Self {
462        Self(x)
463    }
464}
465
466impl From<u8> for Offset32 {
467    fn from(val: u8) -> Offset32 {
468        Self(val.into())
469    }
470}
471
472impl Display for Offset32 {
473    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
474        // 0 displays as an empty offset.
475        if self.0 == 0 {
476            return Ok(());
477        }
478
479        // Always include a sign.
480        write!(f, "{}", if self.0 < 0 { '-' } else { '+' })?;
481
482        let val = i64::from(self.0).abs();
483        if val < 10_000 {
484            write!(f, "{val}")
485        } else {
486            write_hex(val as u64, f)
487        }
488    }
489}
490
491impl FromStr for Offset32 {
492    type Err = &'static str;
493
494    // Parse a decimal or hexadecimal `Offset32`, formatted as above.
495    fn from_str(s: &str) -> Result<Self, &'static str> {
496        if !(s.starts_with('-') || s.starts_with('+')) {
497            return Err("Offset must begin with sign");
498        }
499        parse_i64(s).and_then(|x| {
500            if i64::from(i32::MIN) <= x && x <= i64::from(i32::MAX) {
501                Ok(Self::new(x as i32))
502            } else {
503                Err("Offset out of range")
504            }
505        })
506    }
507}
508
509// FIXME(rust-lang/rust#83527): Replace with `${ignore()}` once it is stabilised.
510macro_rules! ignore {
511    ($($t:tt)*) => {};
512}
513
514macro_rules! ieee_float {
515    (
516        name = $name:ident,
517        bits = $bits:literal,
518        significand_bits = $significand_bits:literal,
519        bits_ty = $bits_ty:ident,
520        float_ty = $float_ty:ident,
521        $(as_float = $as_float:ident,)?
522        $(rust_type_not_stable = $rust_type_not_stable:ident,)?
523    ) => {
524        /// An IEEE
525        #[doc = concat!("binary", stringify!($bits))]
526        /// immediate floating point value, represented as a
527        #[doc = stringify!($bits_ty)]
528        /// containing the bit pattern.
529        ///
530        /// We specifically avoid using a
531        #[doc = stringify!($float_ty)]
532        /// here since some architectures may silently alter floats.
533        /// See: <https://github.com/bytecodealliance/wasmtime/pull/2251#discussion_r498508646>
534        ///
535        /// The [PartialEq] and [Hash] implementations are over the underlying bit pattern, but
536        /// [PartialOrd] respects IEEE754 semantics.
537        ///
538        /// All bit patterns are allowed.
539        #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
540        #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
541        #[repr(C)]
542        pub struct $name {
543            bits: $bits_ty
544        }
545
546        impl $name {
547            const BITS: u8 = $bits;
548            const SIGNIFICAND_BITS: u8 = $significand_bits;
549            const EXPONENT_BITS: u8 = Self::BITS - Self::SIGNIFICAND_BITS - 1;
550            const SIGN_MASK: $bits_ty = 1 << (Self::EXPONENT_BITS + Self::SIGNIFICAND_BITS);
551            const SIGNIFICAND_MASK: $bits_ty = $bits_ty::MAX >> (Self::EXPONENT_BITS + 1);
552            const EXPONENT_MASK: $bits_ty = !Self::SIGN_MASK & !Self::SIGNIFICAND_MASK;
553            /// The positive WebAssembly canonical NaN.
554            pub const NAN: Self = Self::with_bits(Self::EXPONENT_MASK | (1 << (Self::SIGNIFICAND_BITS - 1)));
555
556            /// Create a new
557            #[doc = concat!("`", stringify!($name), "`")]
558            /// containing the bits of `bits`.
559            pub const fn with_bits(bits: $bits_ty) -> Self {
560                Self { bits }
561            }
562
563            /// Get the bitwise representation.
564            pub fn bits(self) -> $bits_ty {
565                self.bits
566            }
567
568            $(
569                /// Create a new
570                #[doc = concat!("`", stringify!($name), "`")]
571                /// representing the number `x`.
572                pub fn with_float(x: $float_ty) -> Self {
573                    Self::with_bits(x.to_bits())
574                }
575
576                /// Converts `self` to a Rust
577                #[doc = concat!("`", stringify!($float_ty), "`.")]
578                pub fn $as_float(self) -> $float_ty {
579                    $float_ty::from_bits(self.bits())
580                }
581            )?
582
583            /// Computes the absolute value of `self`.
584            pub fn abs(self) -> Self {
585                Self::with_bits(self.bits() & !Self::SIGN_MASK)
586            }
587
588            /// Returns a number composed of the magnitude of `self` and the sign of `sign`.
589            pub fn copysign(self, sign: Self) -> Self {
590                Self::with_bits((self.bits() & !Self::SIGN_MASK) | (sign.bits() & Self::SIGN_MASK))
591            }
592
593            /// Returns the minimum of `self` and `other`, following the WebAssembly/IEEE 754-2019 definition.
594            pub fn minimum(self, other: Self) -> Self {
595                // FIXME: Replace with Rust float method once it is stabilised.
596                if self.is_nan() || other.is_nan() {
597                    Self::NAN
598                } else if self.is_zero() && other.is_zero() {
599                    if self.is_negative() {
600                        self
601                    } else {
602                        other
603                    }
604                } else if self <= other {
605                    self
606                } else {
607                    other
608                }
609            }
610
611            /// Returns the maximum of `self` and `other`, following the WebAssembly/IEEE 754-2019 definition.
612            pub fn maximum(self, other: Self) -> Self {
613                // FIXME: Replace with Rust float method once it is stabilised.
614                if self.is_nan() || other.is_nan() {
615                    Self::NAN
616                } else if self.is_zero() && other.is_zero() {
617                    if self.is_positive() {
618                        self
619                    } else {
620                        other
621                    }
622                } else if self >= other {
623                    self
624                } else {
625                    other
626                }
627            }
628
629            /// Create an
630            #[doc = concat!("`", stringify!($name), "`")]
631            /// number representing `2.0^n`.
632            pub fn pow2<I: Into<i32>>(n: I) -> Self {
633                let n = n.into();
634                let w = Self::EXPONENT_BITS;
635                let t = Self::SIGNIFICAND_BITS;
636                let bias = (1 << (w - 1)) - 1;
637                let exponent = n + bias;
638                assert!(exponent > 0, "Underflow n={}", n);
639                assert!(exponent < (1 << w) + 1, "Overflow n={}", n);
640                Self::with_bits((exponent as $bits_ty) << t)
641            }
642
643            /// Create an
644            #[doc = concat!("`", stringify!($name), "`")]
645            /// number representing the greatest negative value not convertible from
646            #[doc = concat!("`", stringify!($float_ty), "`")]
647            /// to a signed integer with width n.
648            pub fn fcvt_to_sint_negative_overflow<I: Into<i32>>(n: I) -> Self {
649                let n = n.into();
650                debug_assert!(n < i32::from(Self::BITS));
651                debug_assert!(i32::from(Self::SIGNIFICAND_BITS) + 1 - n < i32::from(Self::BITS));
652                Self::with_bits((1 << (Self::BITS - 1)) | Self::pow2(n - 1).bits() | (1 << (i32::from(Self::SIGNIFICAND_BITS) + 1 - n)))
653            }
654
655            /// Check if the value is a NaN. For
656            #[doc = concat!("`", stringify!($name), "`,")]
657            /// this means checking that all the exponent bits are set and the significand is non-zero.
658            pub fn is_nan(self) -> bool {
659                self.abs().bits() > Self::EXPONENT_MASK
660            }
661
662            /// Returns true if `self` has a negative sign, including 0.0, NaNs with positive sign bit and positive infinity.
663            pub fn is_positive(self) -> bool {
664                !self.is_negative()
665            }
666
667            /// Returns true if `self` has a negative sign, including -0.0, NaNs with negative sign bit and negative infinity.
668            pub fn is_negative(self) -> bool {
669                self.bits() & Self::SIGN_MASK == Self::SIGN_MASK
670            }
671
672            /// Returns `true` if `self` is positive or negative zero.
673            pub fn is_zero(self) -> bool {
674                self.abs().bits() == 0
675            }
676
677            /// Returns `None` if `self` is a NaN and `Some(self)` otherwise.
678            pub fn non_nan(self) -> Option<Self> {
679                Some(self).filter(|f| !f.is_nan())
680            }
681
682            $(
683                /// Returns the square root of `self`.
684                pub fn sqrt(self) -> Self {
685                    Self::with_float(Libm::<$float_ty>::sqrt(self.$as_float()))
686                }
687
688                /// Returns the smallest integer greater than or equal to `self`.
689                pub fn ceil(self) -> Self {
690                    Self::with_float(Libm::<$float_ty>::ceil(self.$as_float()))
691                }
692
693                /// Returns the largest integer less than or equal to `self`.
694                pub fn floor(self) -> Self {
695                    Self::with_float(Libm::<$float_ty>::floor(self.$as_float()))
696                }
697
698                /// Returns the integer part of `self`. This means that non-integer numbers are always truncated towards zero.
699                pub fn trunc(self) -> Self {
700                    Self::with_float(Libm::<$float_ty>::trunc(self.$as_float()))
701                }
702
703                /// Returns the nearest integer to `self`. Rounds half-way cases to the number
704                /// with an even least significant digit.
705                pub fn round_ties_even(self) -> Self {
706                    Self::with_float(Libm::<$float_ty>::roundeven(self.$as_float()))
707                }
708            )?
709        }
710
711        impl PartialOrd for $name {
712            fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
713                $(self.$as_float().partial_cmp(&rhs.$as_float()))?
714                $(
715                    ignore!($rust_type_not_stable);
716                    // FIXME(#8312): Use builtin Rust comparisons once `f16` and `f128` support is stabalised.
717                    if self.is_nan() || rhs.is_nan() {
718                        // One of the floats is a NaN.
719                        return None;
720                    }
721                    if self.is_zero() && rhs.is_zero() {
722                        // Zeros are always equal regardless of sign.
723                        return Some(Ordering::Equal);
724                    }
725                    let lhs_positive = self.is_positive();
726                    let rhs_positive = rhs.is_positive();
727                    if lhs_positive != rhs_positive {
728                        // Different signs: negative < positive
729                        return lhs_positive.partial_cmp(&rhs_positive);
730                    }
731                    // Finite or infinity will order correctly with an integer comparison of the bits.
732                    if lhs_positive {
733                        self.bits().partial_cmp(&rhs.bits())
734                    } else {
735                        // Reverse the comparison when both floats are negative.
736                        rhs.bits().partial_cmp(&self.bits())
737                    }
738                )?
739            }
740        }
741
742        impl Display for $name {
743            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
744                format_float(u128::from(self.bits()), Self::EXPONENT_BITS, Self::SIGNIFICAND_BITS, f)
745            }
746        }
747
748        impl FromStr for $name {
749            type Err = &'static str;
750
751            fn from_str(s: &str) -> Result<Self, &'static str> {
752                match parse_float(s, Self::EXPONENT_BITS, Self::SIGNIFICAND_BITS) {
753                    Ok(b) => Ok(Self::with_bits(b.try_into().unwrap())),
754                    Err(s) => Err(s),
755                }
756            }
757        }
758
759        impl IntoBytes for $name {
760            fn into_bytes(self) -> Vec<u8> {
761                self.bits().to_le_bytes().to_vec()
762            }
763        }
764
765        impl Neg for $name {
766            type Output = Self;
767
768            fn neg(self) -> Self {
769                Self::with_bits(self.bits() ^ Self::SIGN_MASK)
770            }
771        }
772
773
774
775        $(
776            impl From<$float_ty> for $name {
777                fn from(x: $float_ty) -> Self {
778                    Self::with_float(x)
779                }
780            }
781
782            impl Add for $name {
783                type Output = Self;
784
785                fn add(self, rhs: Self) -> Self {
786                    Self::with_float(self.$as_float() + rhs.$as_float())
787                }
788            }
789
790            impl Sub for $name {
791                type Output = Self;
792
793                fn sub(self, rhs: Self) -> Self {
794                    Self::with_float(self.$as_float() - rhs.$as_float())
795                }
796            }
797
798            impl Mul for $name {
799                type Output = Self;
800
801                fn mul(self, rhs: Self) -> Self {
802                    Self::with_float(self.$as_float() * rhs.$as_float())
803                }
804            }
805
806            impl Div for $name {
807                type Output = Self;
808
809                fn div(self, rhs: Self) -> Self::Output {
810                    Self::with_float(self.$as_float() / rhs.$as_float())
811                }
812            }
813        )?
814
815        impl BitAnd for $name {
816            type Output = Self;
817
818            fn bitand(self, rhs: Self) -> Self {
819                Self::with_bits(self.bits() & rhs.bits())
820            }
821        }
822
823        impl BitOr for $name {
824            type Output = Self;
825
826            fn bitor(self, rhs: Self) -> Self {
827                Self::with_bits(self.bits() | rhs.bits())
828            }
829        }
830
831        impl BitXor for $name {
832            type Output = Self;
833
834            fn bitxor(self, rhs: Self) -> Self {
835                Self::with_bits(self.bits() ^ rhs.bits())
836            }
837        }
838
839        impl Not for $name {
840            type Output = Self;
841
842            fn not(self) -> Self {
843                Self::with_bits(!self.bits())
844            }
845        }
846    };
847}
848
849ieee_float! {
850    name = Ieee16,
851    bits = 16,
852    significand_bits = 10,
853    bits_ty = u16,
854    float_ty = f16,
855    rust_type_not_stable = rust_type_not_stable,
856}
857
858impl Ieee16 {
859    /// Widen to `f32`. Exact: every binary16 is representable in binary32.
860    pub fn to_f32(self) -> f32 {
861        let bits = u32::from(self.bits());
862        let sign = (bits & 0x8000) << 16;
863        let exponent = (bits >> 10) & 0x1f;
864        let significand = bits & 0x03ff;
865
866        let (f32_exponent, f32_significand) = match exponent {
867            0 if significand == 0 => return f32::from_bits(sign),
868            // A subnormal is `significand * 2^-24`.
869            0 => {
870                let msb = significand.ilog2();
871                ((msb + 103) << 23, (significand - (1 << msb)) << (23 - msb))
872            }
873            0x1f => (0x7f80_0000, (significand << 13) & 0x007f_ffff),
874            _ => ((exponent + 112) << 23, significand << 13),
875        };
876        let quiet = if exponent == 0x1f && f32_significand != 0 {
877            0x0040_0000
878        } else {
879            0
880        };
881        f32::from_bits(sign | f32_exponent | f32_significand | quiet)
882    }
883
884    /// Narrow from `f32`, rounding to nearest with ties to even.
885    pub fn from_f32_rne(x: f32) -> Self {
886        let bits = x.to_bits();
887        let sign = ((bits >> 16) & 0x8000) as u16;
888        let exponent = ((bits >> 23) & 0xff) as i32;
889        let significand = bits & 0x007f_ffff;
890
891        match exponent {
892            0xff => Self::infinity_or_nan(sign, significand != 0, (significand >> 13) as u64),
893            // Zero or subnormal: no implicit one, and too small to matter.
894            0 => Self::with_bits(sign),
895            _ => Self::from_parts(sign, exponent - 127, u64::from(significand | 1 << 23), 23),
896        }
897    }
898
899    /// Narrow from `f64`. Must not go via `f32`, which rounds twice.
900    pub fn from_f64_rne(x: f64) -> Self {
901        let bits = x.to_bits();
902        let sign = ((bits >> 48) & 0x8000) as u16;
903        let exponent = ((bits >> 52) & 0x7ff) as i32;
904        let significand = bits & 0x000f_ffff_ffff_ffff;
905
906        match exponent {
907            0x7ff => Self::infinity_or_nan(sign, significand != 0, significand >> 42),
908            0 => Self::with_bits(sign),
909            _ => Self::from_parts(sign, exponent - 1023, significand | 1 << 52, 52),
910        }
911    }
912
913    fn infinity_or_nan(sign: u16, is_nan: bool, payload: u64) -> Self {
914        if !is_nan {
915            return Self::with_bits(sign | 0x7c00);
916        }
917        Self::with_bits(sign | 0x7c00 | (payload as u16 & 0x03ff) | 0x0200)
918    }
919
920    /// Round `significand * 2^(unbiased - significand_bits)` to binary16.
921    fn from_parts(sign: u16, unbiased: i32, significand: u64, significand_bits: u32) -> Self {
922        if unbiased > 15 {
923            return Self::with_bits(sign | 0x7c00);
924        }
925
926        if unbiased >= -14 {
927            // Ordinary. Drop enough to leave the implicit one plus 10 bits.
928            let drop = significand_bits - 10;
929            let mut half_significand = ((significand >> drop) & 0x3ff) as u16;
930            let remainder = significand & ((1 << drop) - 1);
931            let halfway = 1 << (drop - 1);
932            let mut half_exponent = (unbiased + 15) as u16;
933            if remainder > halfway || (remainder == halfway && half_significand & 1 == 1) {
934                half_significand += 1;
935                if half_significand == 0x400 {
936                    // The carry may overflow the exponent.
937                    half_significand = 0;
938                    half_exponent += 1;
939                    if half_exponent >= 0x1f {
940                        return Self::with_bits(sign | 0x7c00);
941                    }
942                }
943            }
944            return Self::with_bits(sign | (half_exponent << 10) | half_significand);
945        }
946
947        if unbiased < -25 {
948            return Self::with_bits(sign);
949        }
950
951        // A binary16 subnormal is `m * 2^-24`.
952        let shift = (significand_bits as i32 - unbiased - 24) as u32;
953        let mut m = (significand >> shift) as u16;
954        let remainder = significand & ((1 << shift) - 1);
955        let halfway = 1 << (shift - 1);
956        if remainder > halfway || (remainder == halfway && m & 1 == 1) {
957            m += 1;
958        }
959        Self::with_bits(sign | m)
960    }
961}
962
963ieee_float! {
964    name = Ieee32,
965    bits = 32,
966    significand_bits = 23,
967    bits_ty = u32,
968    float_ty = f32,
969    as_float = as_f32,
970}
971
972ieee_float! {
973    name = Ieee64,
974    bits = 64,
975    significand_bits = 52,
976    bits_ty = u64,
977    float_ty = f64,
978    as_float = as_f64,
979}
980
981ieee_float! {
982    name = Ieee128,
983    bits = 128,
984    significand_bits = 112,
985    bits_ty = u128,
986    float_ty = f128,
987    rust_type_not_stable = rust_type_not_stable,
988}
989
990/// Format a floating point number in a way that is reasonably human-readable, and that can be
991/// converted back to binary without any rounding issues. The hexadecimal formatting of normal and
992/// subnormal numbers is compatible with C99 and the `printf "%a"` format specifier. The NaN and Inf
993/// formats are not supported by C99.
994///
995/// The encoding parameters are:
996///
997/// w - exponent field width in bits
998/// t - trailing significand field width in bits
999///
1000fn format_float(bits: u128, w: u8, t: u8, f: &mut Formatter) -> fmt::Result {
1001    debug_assert!(w > 0 && w <= 16, "Invalid exponent range");
1002    debug_assert!(1 + w + t <= 128, "Too large IEEE format for u128");
1003    debug_assert!((t + w + 1).is_power_of_two(), "Unexpected IEEE format size");
1004
1005    let max_e_bits = (1u128 << w) - 1;
1006    let t_bits = bits & ((1u128 << t) - 1); // Trailing significand.
1007    let e_bits = (bits >> t) & max_e_bits; // Biased exponent.
1008    let sign_bit = (bits >> (w + t)) & 1;
1009
1010    let bias: i32 = (1 << (w - 1)) - 1;
1011    let e = e_bits as i32 - bias; // Unbiased exponent.
1012    let emin = 1 - bias; // Minimum exponent.
1013
1014    // How many hexadecimal digits are needed for the trailing significand?
1015    let digits = (t + 3) / 4;
1016    // Trailing significand left-aligned in `digits` hexadecimal digits.
1017    let left_t_bits = t_bits << (4 * digits - t);
1018
1019    // All formats share the leading sign.
1020    if sign_bit != 0 {
1021        write!(f, "-")?;
1022    }
1023
1024    if e_bits == 0 {
1025        if t_bits == 0 {
1026            // Zero.
1027            write!(f, "0.0")
1028        } else {
1029            // Subnormal.
1030            write!(
1031                f,
1032                "0x0.{0:01$x}p{2}",
1033                left_t_bits,
1034                usize::from(digits),
1035                emin
1036            )
1037        }
1038    } else if e_bits == max_e_bits {
1039        // Always print a `+` or `-` sign for these special values.
1040        // This makes them easier to parse as they can't be confused as identifiers.
1041        if sign_bit == 0 {
1042            write!(f, "+")?;
1043        }
1044        if t_bits == 0 {
1045            // Infinity.
1046            write!(f, "Inf")
1047        } else {
1048            // NaN.
1049            let payload = t_bits & ((1 << (t - 1)) - 1);
1050            if t_bits & (1 << (t - 1)) != 0 {
1051                // Quiet NaN.
1052                if payload != 0 {
1053                    write!(f, "NaN:0x{payload:x}")
1054                } else {
1055                    write!(f, "NaN")
1056                }
1057            } else {
1058                // Signaling NaN.
1059                write!(f, "sNaN:0x{payload:x}")
1060            }
1061        }
1062    } else {
1063        // Normal number.
1064        write!(f, "0x1.{0:01$x}p{2}", left_t_bits, usize::from(digits), e)
1065    }
1066}
1067
1068/// Parse a float using the same format as `format_float` above.
1069///
1070/// The encoding parameters are:
1071///
1072/// w - exponent field width in bits
1073/// t - trailing significand field width in bits
1074///
1075fn parse_float(s: &str, w: u8, t: u8) -> Result<u128, &'static str> {
1076    debug_assert!(w > 0 && w <= 16, "Invalid exponent range");
1077    debug_assert!(1 + w + t <= 128, "Too large IEEE format for u128");
1078    debug_assert!((t + w + 1).is_power_of_two(), "Unexpected IEEE format size");
1079
1080    let (sign_bit, s2) = if let Some(num) = s.strip_prefix('-') {
1081        (1u128 << (t + w), num)
1082    } else if let Some(num) = s.strip_prefix('+') {
1083        (0, num)
1084    } else {
1085        (0, s)
1086    };
1087
1088    if !s2.starts_with("0x") {
1089        let max_e_bits = ((1u128 << w) - 1) << t;
1090        let quiet_bit = 1u128 << (t - 1);
1091
1092        // The only decimal encoding allowed is 0.
1093        if s2 == "0.0" {
1094            return Ok(sign_bit);
1095        }
1096
1097        if s2 == "Inf" {
1098            // +/- infinity: e = max, t = 0.
1099            return Ok(sign_bit | max_e_bits);
1100        }
1101        if s2 == "NaN" {
1102            // Canonical quiet NaN: e = max, t = quiet.
1103            return Ok(sign_bit | max_e_bits | quiet_bit);
1104        }
1105        if let Some(nan) = s2.strip_prefix("NaN:0x") {
1106            // Quiet NaN with payload.
1107            return match u128::from_str_radix(nan, 16) {
1108                Ok(payload) if payload < quiet_bit => {
1109                    Ok(sign_bit | max_e_bits | quiet_bit | payload)
1110                }
1111                _ => Err("Invalid NaN payload"),
1112            };
1113        }
1114        if let Some(nan) = s2.strip_prefix("sNaN:0x") {
1115            // Signaling NaN with payload.
1116            return match u128::from_str_radix(nan, 16) {
1117                Ok(payload) if 0 < payload && payload < quiet_bit => {
1118                    Ok(sign_bit | max_e_bits | payload)
1119                }
1120                _ => Err("Invalid sNaN payload"),
1121            };
1122        }
1123
1124        return Err("Float must be hexadecimal");
1125    }
1126    let s3 = &s2[2..];
1127
1128    let mut digits = 0u8;
1129    let mut digits_before_period: Option<u8> = None;
1130    let mut significand = 0u128;
1131    let mut exponent = 0i32;
1132
1133    for (idx, ch) in s3.char_indices() {
1134        match ch {
1135            '.' => {
1136                // This is the radix point. There can only be one.
1137                if digits_before_period != None {
1138                    return Err("Multiple radix points");
1139                } else {
1140                    digits_before_period = Some(digits);
1141                }
1142            }
1143            'p' => {
1144                // The following exponent is a decimal number.
1145                let exp_str = &s3[1 + idx..];
1146                match exp_str.parse::<i16>() {
1147                    Ok(e) => {
1148                        exponent = i32::from(e);
1149                        break;
1150                    }
1151                    Err(_) => return Err("Bad exponent"),
1152                }
1153            }
1154            _ => match ch.to_digit(16) {
1155                Some(digit) => {
1156                    digits += 1;
1157                    if digits > 32 {
1158                        return Err("Too many digits");
1159                    }
1160                    significand = (significand << 4) | u128::from(digit);
1161                }
1162                None => return Err("Invalid character"),
1163            },
1164        }
1165    }
1166
1167    if digits == 0 {
1168        return Err("No digits");
1169    }
1170
1171    if significand == 0 {
1172        // This is +/- 0.0.
1173        return Ok(sign_bit);
1174    }
1175
1176    // Number of bits appearing after the radix point.
1177    match digits_before_period {
1178        None => {} // No radix point present.
1179        Some(d) => exponent -= 4 * i32::from(digits - d),
1180    };
1181
1182    // Normalize the significand and exponent.
1183    let significant_bits = (128 - significand.leading_zeros()) as u8;
1184    if significant_bits > t + 1 {
1185        let adjust = significant_bits - (t + 1);
1186        if significand & ((1u128 << adjust) - 1) != 0 {
1187            return Err("Too many significant bits");
1188        }
1189        // Adjust significand down.
1190        significand >>= adjust;
1191        exponent += i32::from(adjust);
1192    } else {
1193        let adjust = t + 1 - significant_bits;
1194        significand <<= adjust;
1195        exponent -= i32::from(adjust);
1196    }
1197    debug_assert_eq!(significand >> t, 1);
1198
1199    // Trailing significand excludes the high bit.
1200    let t_bits = significand & ((1 << t) - 1);
1201
1202    let max_exp = (1i32 << w) - 2;
1203    let bias: i32 = (1 << (w - 1)) - 1;
1204    exponent += bias + i32::from(t);
1205
1206    if exponent > max_exp {
1207        Err("Magnitude too large")
1208    } else if exponent > 0 {
1209        // This is a normal number.
1210        let e_bits = (exponent as u128) << t;
1211        Ok(sign_bit | e_bits | t_bits)
1212    } else if 1 - exponent <= i32::from(t) {
1213        // This is a subnormal number: e = 0, t = significand bits.
1214        // Renormalize significand for exponent = 1.
1215        let adjust = 1 - exponent;
1216        if significand & ((1u128 << adjust) - 1) != 0 {
1217            Err("Subnormal underflow")
1218        } else {
1219            significand >>= adjust;
1220            Ok(sign_bit | significand)
1221        }
1222    } else {
1223        Err("Magnitude too small")
1224    }
1225}
1226
1227#[cfg(test)]
1228mod tests {
1229    use super::*;
1230    use alloc::string::ToString;
1231
1232    #[test]
1233    fn format_imm64() {
1234        assert_eq!(Imm64(0).to_string(), "0");
1235        assert_eq!(Imm64(9999).to_string(), "9999");
1236        assert_eq!(Imm64(10000).to_string(), "0x2710");
1237        assert_eq!(Imm64(-9999).to_string(), "-9999");
1238        assert_eq!(Imm64(-10000).to_string(), "-10000");
1239        assert_eq!(Imm64(0xffff).to_string(), "0xffff");
1240        assert_eq!(Imm64(0x10000).to_string(), "0x0001_0000");
1241    }
1242
1243    #[test]
1244    fn format_uimm64() {
1245        assert_eq!(Uimm64(0).to_string(), "0");
1246        assert_eq!(Uimm64(9999).to_string(), "9999");
1247        assert_eq!(Uimm64(10000).to_string(), "0x2710");
1248        assert_eq!(Uimm64(-9999i64 as u64).to_string(), "0xffff_ffff_ffff_d8f1");
1249        assert_eq!(
1250            Uimm64(-10000i64 as u64).to_string(),
1251            "0xffff_ffff_ffff_d8f0"
1252        );
1253        assert_eq!(Uimm64(0xffff).to_string(), "0xffff");
1254        assert_eq!(Uimm64(0x10000).to_string(), "0x0001_0000");
1255    }
1256
1257    // Verify that `text` can be parsed as a `T` into a value that displays as `want`.
1258    #[track_caller]
1259    fn parse_ok<T: FromStr + Display>(text: &str, want: &str)
1260    where
1261        <T as FromStr>::Err: Display,
1262    {
1263        match text.parse::<T>() {
1264            Err(s) => panic!("\"{text}\".parse() error: {s}"),
1265            Ok(x) => assert_eq!(x.to_string(), want),
1266        }
1267    }
1268
1269    // Verify that `text` fails to parse as `T` with the error `msg`.
1270    fn parse_err<T: FromStr + Display>(text: &str, msg: &str)
1271    where
1272        <T as FromStr>::Err: Display,
1273    {
1274        match text.parse::<T>() {
1275            Err(s) => assert_eq!(s.to_string(), msg),
1276            Ok(x) => panic!("Wanted Err({msg}), but got {x}"),
1277        }
1278    }
1279
1280    #[test]
1281    fn parse_imm64() {
1282        parse_ok::<Imm64>("0", "0");
1283        parse_ok::<Imm64>("1", "1");
1284        parse_ok::<Imm64>("-0", "0");
1285        parse_ok::<Imm64>("-1", "-1");
1286        parse_ok::<Imm64>("0x0", "0");
1287        parse_ok::<Imm64>("0xf", "15");
1288        parse_ok::<Imm64>("-0x9", "-9");
1289
1290        // Probe limits.
1291        parse_ok::<Imm64>("0xffffffff_ffffffff", "-1");
1292        parse_ok::<Imm64>("0x80000000_00000000", "-9223372036854775808");
1293        parse_ok::<Imm64>("-0x80000000_00000000", "-9223372036854775808");
1294        parse_err::<Imm64>("-0x80000000_00000001", "Negative number too small");
1295        parse_ok::<Imm64>("18446744073709551615", "-1");
1296        parse_ok::<Imm64>("-9223372036854775808", "-9223372036854775808");
1297        // Overflow both the `checked_add` and `checked_mul`.
1298        parse_err::<Imm64>("18446744073709551616", "Too large decimal number");
1299        parse_err::<Imm64>("184467440737095516100", "Too large decimal number");
1300        parse_err::<Imm64>("-9223372036854775809", "Negative number too small");
1301
1302        // Underscores are allowed where digits go.
1303        parse_ok::<Imm64>("0_0", "0");
1304        parse_ok::<Imm64>("-_10_0", "-100");
1305        parse_ok::<Imm64>("_10_", "10");
1306        parse_ok::<Imm64>("0x97_88_bb", "0x0097_88bb");
1307        parse_ok::<Imm64>("0x_97_", "151");
1308
1309        parse_err::<Imm64>("", "No digits in number");
1310        parse_err::<Imm64>("-", "No digits in number");
1311        parse_err::<Imm64>("_", "No digits in number");
1312        parse_err::<Imm64>("0x", "No digits in number");
1313        parse_err::<Imm64>("0x_", "No digits in number");
1314        parse_err::<Imm64>("-0x", "No digits in number");
1315        parse_err::<Imm64>(" ", "Invalid character in decimal number");
1316        parse_err::<Imm64>("0 ", "Invalid character in decimal number");
1317        parse_err::<Imm64>(" 0", "Invalid character in decimal number");
1318        parse_err::<Imm64>("--", "Invalid character in decimal number");
1319        parse_err::<Imm64>("-0x-", "Invalid character in hexadecimal number");
1320        parse_err::<Imm64>("abc", "Invalid character in decimal number");
1321        parse_err::<Imm64>("-abc", "Invalid character in decimal number");
1322
1323        // Hex count overflow.
1324        parse_err::<Imm64>("0x0_0000_0000_0000_0000", "Too many hexadecimal digits");
1325    }
1326
1327    #[test]
1328    fn parse_uimm64() {
1329        parse_ok::<Uimm64>("0", "0");
1330        parse_ok::<Uimm64>("1", "1");
1331        parse_ok::<Uimm64>("0x0", "0");
1332        parse_ok::<Uimm64>("0xf", "15");
1333        parse_ok::<Uimm64>("0xffffffff_fffffff7", "0xffff_ffff_ffff_fff7");
1334
1335        // Probe limits.
1336        parse_ok::<Uimm64>("0xffffffff_ffffffff", "0xffff_ffff_ffff_ffff");
1337        parse_ok::<Uimm64>("0x80000000_00000000", "0x8000_0000_0000_0000");
1338        parse_ok::<Uimm64>("18446744073709551615", "0xffff_ffff_ffff_ffff");
1339        // Overflow both the `checked_add` and `checked_mul`.
1340        parse_err::<Uimm64>("18446744073709551616", "Too large decimal number");
1341        parse_err::<Uimm64>("184467440737095516100", "Too large decimal number");
1342
1343        // Underscores are allowed where digits go.
1344        parse_ok::<Uimm64>("0_0", "0");
1345        parse_ok::<Uimm64>("_10_", "10");
1346        parse_ok::<Uimm64>("0x97_88_bb", "0x0097_88bb");
1347        parse_ok::<Uimm64>("0x_97_", "151");
1348
1349        parse_err::<Uimm64>("", "No digits in number");
1350        parse_err::<Uimm64>("_", "No digits in number");
1351        parse_err::<Uimm64>("0x", "No digits in number");
1352        parse_err::<Uimm64>("0x_", "No digits in number");
1353        parse_err::<Uimm64>("-", "Invalid character in decimal number");
1354        parse_err::<Uimm64>("-0x", "Invalid character in hexadecimal number");
1355        parse_err::<Uimm64>(" ", "Invalid character in decimal number");
1356        parse_err::<Uimm64>("0 ", "Invalid character in decimal number");
1357        parse_err::<Uimm64>(" 0", "Invalid character in decimal number");
1358        parse_err::<Uimm64>("--", "Invalid character in decimal number");
1359        parse_err::<Uimm64>("-0x-", "Invalid character in hexadecimal number");
1360        parse_err::<Uimm64>("-0", "Invalid character in decimal number");
1361        parse_err::<Uimm64>("-1", "Invalid character in decimal number");
1362        parse_err::<Uimm64>("abc", "Invalid character in decimal number");
1363        parse_err::<Uimm64>("-abc", "Invalid character in decimal number");
1364
1365        // Hex count overflow.
1366        parse_err::<Uimm64>("0x0_0000_0000_0000_0000", "Too many hexadecimal digits");
1367    }
1368
1369    #[test]
1370    fn format_offset32() {
1371        assert_eq!(Offset32(0).to_string(), "");
1372        assert_eq!(Offset32(1).to_string(), "+1");
1373        assert_eq!(Offset32(-1).to_string(), "-1");
1374        assert_eq!(Offset32(9999).to_string(), "+9999");
1375        assert_eq!(Offset32(10000).to_string(), "+0x2710");
1376        assert_eq!(Offset32(-9999).to_string(), "-9999");
1377        assert_eq!(Offset32(-10000).to_string(), "-0x2710");
1378        assert_eq!(Offset32(0xffff).to_string(), "+0xffff");
1379        assert_eq!(Offset32(0x10000).to_string(), "+0x0001_0000");
1380    }
1381
1382    #[test]
1383    fn parse_offset32() {
1384        parse_ok::<Offset32>("+0", "");
1385        parse_ok::<Offset32>("+1", "+1");
1386        parse_ok::<Offset32>("-0", "");
1387        parse_ok::<Offset32>("-1", "-1");
1388        parse_ok::<Offset32>("+0x0", "");
1389        parse_ok::<Offset32>("+0xf", "+15");
1390        parse_ok::<Offset32>("-0x9", "-9");
1391        parse_ok::<Offset32>("-0x8000_0000", "-0x8000_0000");
1392
1393        parse_err::<Offset32>("+0x8000_0000", "Offset out of range");
1394    }
1395
1396    #[test]
1397    fn format_ieee16() {
1398        assert_eq!(Ieee16::with_bits(0).to_string(), "0.0"); // 0.0
1399        assert_eq!(Ieee16::with_bits(0x8000).to_string(), "-0.0"); // -0.0
1400        assert_eq!(Ieee16::with_bits(0x3c00).to_string(), "0x1.000p0"); // 1.0
1401        assert_eq!(Ieee16::with_bits(0x3e00).to_string(), "0x1.800p0"); // 1.5
1402        assert_eq!(Ieee16::with_bits(0x3800).to_string(), "0x1.000p-1"); // 0.5
1403        assert_eq!(
1404            Ieee16::with_bits(0x1400).to_string(), // `f16::EPSILON`
1405            "0x1.000p-10"
1406        );
1407        assert_eq!(
1408            Ieee16::with_bits(0xfbff).to_string(), // `f16::MIN`
1409            "-0x1.ffcp15"
1410        );
1411        assert_eq!(
1412            Ieee16::with_bits(0x7bff).to_string(), // `f16::MAX`
1413            "0x1.ffcp15"
1414        );
1415        // Smallest positive normal number.
1416        assert_eq!(
1417            Ieee16::with_bits(0x0400).to_string(), // `f16::MIN_POSITIVE`
1418            "0x1.000p-14"
1419        );
1420        // Subnormals.
1421        assert_eq!(
1422            Ieee16::with_bits(0x0200).to_string(), // `f16::MIN_POSITIVE / 2.0`
1423            "0x0.800p-14"
1424        );
1425        assert_eq!(
1426            Ieee16::with_bits(0x0001).to_string(), // `f16::MIN_POSITIVE * f16::EPSILON`
1427            "0x0.004p-14"
1428        );
1429        assert_eq!(
1430            Ieee16::with_bits(0x7c00).to_string(), // `f16::INFINITY`
1431            "+Inf"
1432        );
1433        assert_eq!(
1434            Ieee16::with_bits(0xfc00).to_string(), // `f16::NEG_INFINITY`
1435            "-Inf"
1436        );
1437        assert_eq!(
1438            Ieee16::with_bits(0x7e00).to_string(), // `f16::NAN`
1439            "+NaN"
1440        );
1441        assert_eq!(
1442            Ieee16::with_bits(0xfe00).to_string(), // `-f16::NAN`
1443            "-NaN"
1444        );
1445        // Construct some qNaNs with payloads.
1446        assert_eq!(Ieee16::with_bits(0x7e01).to_string(), "+NaN:0x1");
1447        assert_eq!(Ieee16::with_bits(0x7f01).to_string(), "+NaN:0x101");
1448        // Signaling NaNs.
1449        assert_eq!(Ieee16::with_bits(0x7c01).to_string(), "+sNaN:0x1");
1450        assert_eq!(Ieee16::with_bits(0x7d01).to_string(), "+sNaN:0x101");
1451    }
1452
1453    #[test]
1454    fn parse_ieee16() {
1455        parse_ok::<Ieee16>("0.0", "0.0");
1456        parse_ok::<Ieee16>("+0.0", "0.0");
1457        parse_ok::<Ieee16>("-0.0", "-0.0");
1458        parse_ok::<Ieee16>("0x0", "0.0");
1459        parse_ok::<Ieee16>("0x0.0", "0.0");
1460        parse_ok::<Ieee16>("0x.0", "0.0");
1461        parse_ok::<Ieee16>("0x0.", "0.0");
1462        parse_ok::<Ieee16>("0x1", "0x1.000p0");
1463        parse_ok::<Ieee16>("+0x1", "0x1.000p0");
1464        parse_ok::<Ieee16>("-0x1", "-0x1.000p0");
1465        parse_ok::<Ieee16>("0x10", "0x1.000p4");
1466        parse_ok::<Ieee16>("0x10.0", "0x1.000p4");
1467        parse_err::<Ieee16>("0.", "Float must be hexadecimal");
1468        parse_err::<Ieee16>(".0", "Float must be hexadecimal");
1469        parse_err::<Ieee16>("0", "Float must be hexadecimal");
1470        parse_err::<Ieee16>("-0", "Float must be hexadecimal");
1471        parse_err::<Ieee16>(".", "Float must be hexadecimal");
1472        parse_err::<Ieee16>("", "Float must be hexadecimal");
1473        parse_err::<Ieee16>("-", "Float must be hexadecimal");
1474        parse_err::<Ieee16>("0x", "No digits");
1475        parse_err::<Ieee16>("0x..", "Multiple radix points");
1476
1477        // Check significant bits.
1478        parse_ok::<Ieee16>("0x0.ffe", "0x1.ffcp-1");
1479        parse_ok::<Ieee16>("0x1.ffc", "0x1.ffcp0");
1480        parse_ok::<Ieee16>("0x3.ff8", "0x1.ffcp1");
1481        parse_ok::<Ieee16>("0x7.ff", "0x1.ffcp2");
1482        parse_ok::<Ieee16>("0xf.fe", "0x1.ffcp3");
1483        parse_err::<Ieee16>("0x1.ffe", "Too many significant bits");
1484        parse_err::<Ieee16>("0x1.ffc00000000000000000000000000000", "Too many digits");
1485
1486        // Exponents.
1487        parse_ok::<Ieee16>("0x1p3", "0x1.000p3");
1488        parse_ok::<Ieee16>("0x1p-3", "0x1.000p-3");
1489        parse_ok::<Ieee16>("0x1.0p3", "0x1.000p3");
1490        parse_ok::<Ieee16>("0x2.0p3", "0x1.000p4");
1491        parse_ok::<Ieee16>("0x1.0p15", "0x1.000p15");
1492        parse_ok::<Ieee16>("0x1.0p-14", "0x1.000p-14");
1493        parse_ok::<Ieee16>("0x0.1p-10", "0x1.000p-14");
1494        parse_err::<Ieee16>("0x2.0p15", "Magnitude too large");
1495
1496        // Subnormals.
1497        parse_ok::<Ieee16>("0x1.0p-15", "0x0.800p-14");
1498        parse_ok::<Ieee16>("0x1.0p-24", "0x0.004p-14");
1499        parse_ok::<Ieee16>("0x0.004p-14", "0x0.004p-14");
1500        parse_err::<Ieee16>("0x0.102p-14", "Subnormal underflow");
1501        parse_err::<Ieee16>("0x1.8p-24", "Subnormal underflow");
1502        parse_err::<Ieee16>("0x1.0p-25", "Magnitude too small");
1503
1504        // NaNs and Infs.
1505        parse_ok::<Ieee16>("Inf", "+Inf");
1506        parse_ok::<Ieee16>("+Inf", "+Inf");
1507        parse_ok::<Ieee16>("-Inf", "-Inf");
1508        parse_ok::<Ieee16>("NaN", "+NaN");
1509        parse_ok::<Ieee16>("+NaN", "+NaN");
1510        parse_ok::<Ieee16>("-NaN", "-NaN");
1511        parse_ok::<Ieee16>("NaN:0x0", "+NaN");
1512        parse_err::<Ieee16>("NaN:", "Float must be hexadecimal");
1513        parse_err::<Ieee16>("NaN:0", "Float must be hexadecimal");
1514        parse_err::<Ieee16>("NaN:0x", "Invalid NaN payload");
1515        parse_ok::<Ieee16>("NaN:0x001", "+NaN:0x1");
1516        parse_ok::<Ieee16>("NaN:0x101", "+NaN:0x101");
1517        parse_err::<Ieee16>("NaN:0x301", "Invalid NaN payload");
1518        parse_ok::<Ieee16>("sNaN:0x1", "+sNaN:0x1");
1519        parse_err::<Ieee16>("sNaN:0x0", "Invalid sNaN payload");
1520        parse_ok::<Ieee16>("sNaN:0x101", "+sNaN:0x101");
1521        parse_err::<Ieee16>("sNaN:0x301", "Invalid sNaN payload");
1522    }
1523
1524    #[test]
1525    fn pow2_ieee16() {
1526        assert_eq!(Ieee16::pow2(0).to_string(), "0x1.000p0");
1527        assert_eq!(Ieee16::pow2(1).to_string(), "0x1.000p1");
1528        assert_eq!(Ieee16::pow2(-1).to_string(), "0x1.000p-1");
1529        assert_eq!(Ieee16::pow2(15).to_string(), "0x1.000p15");
1530        assert_eq!(Ieee16::pow2(-14).to_string(), "0x1.000p-14");
1531
1532        assert_eq!((-Ieee16::pow2(1)).to_string(), "-0x1.000p1");
1533    }
1534
1535    #[test]
1536    fn partial_cmp_zero_ieee16() {
1537        let zero = Ieee16::with_bits(0);
1538        let one = Ieee16::pow2(0);
1539        let subnormal = Ieee16::with_bits(1);
1540
1541        for zero in [zero, -zero] {
1542            assert_eq!(zero.partial_cmp(&zero), Some(Ordering::Equal));
1543            assert_eq!(zero.partial_cmp(&-zero), Some(Ordering::Equal));
1544
1545            for (value, ordering) in [
1546                (one, Ordering::Greater),
1547                (-one, Ordering::Less),
1548                (subnormal, Ordering::Greater),
1549                (-subnormal, Ordering::Less),
1550            ] {
1551                assert_eq!(
1552                    value.partial_cmp(&zero),
1553                    Some(ordering),
1554                    "{value} compared with {zero}"
1555                );
1556                assert_eq!(
1557                    zero.partial_cmp(&value),
1558                    Some(ordering.reverse()),
1559                    "{zero} compared with {value}"
1560                );
1561            }
1562        }
1563    }
1564
1565    #[test]
1566    fn fcvt_to_sint_negative_overflow_ieee16() {
1567        // FIXME(#8312): Replace with commented out version once Rust f16 support is stabilised.
1568        // let n = 8;
1569        // assert_eq!(
1570        //     -((1u16 << (n - 1)) as f16) - 1.0,
1571        //     Ieee16::fcvt_to_sint_negative_overflow(n).as_f16()
1572        // );
1573        let n = 8;
1574        assert_eq!(
1575            "-0x1.020p7",
1576            Ieee16::fcvt_to_sint_negative_overflow(n).to_string()
1577        );
1578    }
1579
1580    /// Known bit patterns, so a widening bug that round-trips is still caught.
1581    #[test]
1582    fn to_f32_known_values_ieee16() {
1583        let cases = [
1584            (0x0000, 0x0000_0000), // +0
1585            (0x8000, 0x8000_0000), // -0
1586            (0x3c00, 0x3f80_0000), // 1
1587            (0xbc00, 0xbf80_0000), // -1
1588            (0x7bff, 0x477f_e000), // 65504, the largest finite value
1589            (0x7c00, 0x7f80_0000), // infinity
1590            (0x0400, 0x3880_0000), // 2^-14, smallest normal
1591            (0x0001, 0x3380_0000), // 2^-24, smallest subnormal
1592            (0x03ff, 0x387f_c000), // 1023 * 2^-24, largest subnormal
1593            (0x0200, 0x3800_0000), // 2^-15
1594        ];
1595        for (input, expected) in cases {
1596            assert_eq!(
1597                Ieee16::with_bits(input).to_f32().to_bits(),
1598                expected,
1599                "bits = {input:#06x}"
1600            );
1601        }
1602    }
1603
1604    /// Every midpoint must round to the neighbour with an even significand.
1605    #[test]
1606    fn every_tie_rounds_to_even_ieee16() {
1607        for bits in 0..u16::MAX {
1608            let (lo, hi) = (Ieee16::with_bits(bits), Ieee16::with_bits(bits + 1));
1609            // Skip the pairs that straddle an infinity or a NaN encoding.
1610            if lo.is_nan() || hi.is_nan() || (lo.bits() & 0x7fff) == 0x7bff {
1611                continue;
1612            }
1613            let even = if bits & 1 == 0 { lo } else { hi };
1614
1615            let (a, b) = (lo.to_f32(), hi.to_f32());
1616            let mid = (a + b) / 2.0;
1617            assert_eq!(
1618                Ieee16::from_f32_rne(mid),
1619                even,
1620                "midpoint of {:#06x} and {:#06x}",
1621                bits,
1622                bits + 1
1623            );
1624
1625            // Which side is nearer depends on the sign, so only check the
1626            // non-negative half.
1627            if bits < 0x8000 {
1628                let up = f32::from_bits(mid.to_bits() + 1);
1629                let down = f32::from_bits(mid.to_bits() - 1);
1630                assert_eq!(Ieee16::from_f32_rne(up), hi, "{bits:#06x} up");
1631                assert_eq!(Ieee16::from_f32_rne(down), lo, "{bits:#06x} down");
1632            }
1633        }
1634    }
1635
1636    /// Round-tripping is the identity, except a NaN comes back quieted.
1637    #[test]
1638    fn round_trip_is_identity_ieee16() {
1639        for bits in 0..=u16::MAX {
1640            let original = Ieee16::with_bits(bits);
1641            let back = Ieee16::from_f32_rne(original.to_f32());
1642            let expected = if original.is_nan() {
1643                Ieee16::with_bits(bits | 0x0200)
1644            } else {
1645                original
1646            };
1647            assert_eq!(back, expected, "bits = {bits:#06x}");
1648        }
1649    }
1650
1651    /// An `f64` that is exactly an `f32` must narrow the same way either way.
1652    #[test]
1653    fn from_f64_rne_agrees_ieee16() {
1654        for bits in 0..u16::MAX {
1655            let value = Ieee16::with_bits(bits);
1656            if value.is_nan() {
1657                continue;
1658            }
1659            let as_f64 = f64::from(value.to_f32());
1660            assert_eq!(
1661                Ieee16::from_f64_rne(as_f64),
1662                Ieee16::from_f32_rne(value.to_f32()),
1663                "bits = {bits:#06x}"
1664            );
1665        }
1666    }
1667
1668    /// The same trap for a fused multiply-add: the exact product and sum needs
1669    /// more than 24 bits.
1670    #[test]
1671    fn fma_does_not_double_round_ieee16() {
1672        let (x, y, z) = (
1673            f64::from(Ieee16::with_bits(0x520b).to_f32()),
1674            f64::from(Ieee16::with_bits(0x00e9).to_f32()),
1675            f64::from(Ieee16::with_bits(0x2ff6).to_f32()),
1676        );
1677        // Exact in f64: the product needs 22 bits, the sum under 34. Not
1678        // `mul_add`, which is not correct on every target.
1679        let exact = x * y + z;
1680        assert_eq!(Ieee16::from_f64_rne(exact).bits(), 0x3001);
1681        assert_eq!(Ieee16::from_f32_rne(exact as f32).bits(), 0x3000);
1682    }
1683
1684    /// A subnormal source has no implicit one, but every binary32 and binary64
1685    /// subnormal is far below the smallest binary16 subnormal.
1686    #[test]
1687    fn subnormal_source_rounds_to_zero_ieee16() {
1688        for bits in [0x0000_0001, 0x003f_ffff, 0x007f_ffff, 0x807f_ffff] {
1689            let x = f32::from_bits(bits);
1690            let expected = if bits >> 31 == 1 { 0x8000 } else { 0x0000 };
1691            assert_eq!(
1692                Ieee16::from_f32_rne(x).bits(),
1693                expected,
1694                "bits = {bits:#010x}"
1695            );
1696        }
1697        for bits in [
1698            0x0000_0000_0000_0001,
1699            0x000f_ffff_ffff_ffff,
1700            0x800f_ffff_ffff_ffff,
1701        ] {
1702            let x = f64::from_bits(bits);
1703            let expected = if bits >> 63 == 1 { 0x8000 } else { 0x0000 };
1704            assert_eq!(
1705                Ieee16::from_f64_rne(x).bits(),
1706                expected,
1707                "bits = {bits:#018x}"
1708            );
1709        }
1710        // Zero itself, including both signs.
1711        assert_eq!(Ieee16::from_f32_rne(0.0).bits(), 0x0000);
1712        assert_eq!(Ieee16::from_f32_rne(-0.0).bits(), 0x8000);
1713        assert_eq!(Ieee16::from_f64_rne(0.0).bits(), 0x0000);
1714        assert_eq!(Ieee16::from_f64_rne(-0.0).bits(), 0x8000);
1715    }
1716
1717    /// Narrowing an `f64` through `f32` can pick the wrong side of a midpoint.
1718    #[test]
1719    fn f64_narrowing_does_not_double_round_ieee16() {
1720        let just_past = 1.0 + 2f64.powi(-11) + 2f64.powi(-40);
1721        assert_eq!(Ieee16::from_f64_rne(just_past).bits(), 0x3c01);
1722        assert_eq!(Ieee16::from_f64_rne(-just_past).bits(), 0xbc01);
1723        assert_eq!(Ieee16::from_f32_rne(just_past as f32).bits(), 0x3c00);
1724    }
1725
1726    #[test]
1727    fn overflow_boundaries_ieee16() {
1728        assert_eq!(Ieee16::from_f32_rne(65504.0).bits(), 0x7bff);
1729        assert_eq!(Ieee16::from_f32_rne(65519.0).bits(), 0x7bff);
1730        // The tie between 65504 and infinity rounds to even, which overflows.
1731        assert_eq!(Ieee16::from_f32_rne(65520.0).bits(), 0x7c00);
1732        assert_eq!(Ieee16::from_f32_rne(65536.0).bits(), 0x7c00);
1733        assert_eq!(Ieee16::from_f32_rne(f32::INFINITY).bits(), 0x7c00);
1734        assert_eq!(Ieee16::from_f32_rne(-f32::INFINITY).bits(), 0xfc00);
1735        assert_eq!(Ieee16::from_f32_rne(1e30).bits(), 0x7c00);
1736    }
1737
1738    #[test]
1739    fn underflow_boundaries_ieee16() {
1740        assert_eq!(Ieee16::from_f32_rne(2f32.powi(-14)).bits(), 0x0400);
1741        assert_eq!(Ieee16::from_f32_rne(2f32.powi(-15)).bits(), 0x0200);
1742        assert_eq!(Ieee16::from_f32_rne(2f32.powi(-24)).bits(), 0x0001);
1743        // 2^-25 is the tie between zero and the smallest subnormal.
1744        assert_eq!(Ieee16::from_f32_rne(2f32.powi(-25)).bits(), 0x0000);
1745        assert_eq!(Ieee16::from_f32_rne(2f32.powi(-26)).bits(), 0x0000);
1746        assert_eq!(Ieee16::from_f32_rne(0.0).bits(), 0x0000);
1747        assert_eq!(Ieee16::from_f32_rne(-0.0).bits(), 0x8000);
1748    }
1749
1750    #[test]
1751    fn subnormal_rounds_up_to_smallest_normal_ieee16() {
1752        assert_eq!(
1753            Ieee16::from_f32_rne(Ieee16::with_bits(0x03ff).to_f32()).bits(),
1754            0x03ff
1755        );
1756        // The midpoint between 0x03ff and 0x0400. 1023 is odd, so the tie
1757        // rounds up to the smallest normal.
1758        assert_eq!(
1759            Ieee16::from_f32_rne(f32::from_bits(0x387f_e000)).bits(),
1760            0x0400
1761        );
1762    }
1763
1764    #[test]
1765    fn nan_is_quieted_ieee16() {
1766        for input in [f32::NAN, -f32::NAN, f32::from_bits(0x7f80_0001)] {
1767            let out = Ieee16::from_f32_rne(input);
1768            assert!(out.is_nan(), "input = {input:e}");
1769        }
1770    }
1771
1772    #[test]
1773    fn format_ieee32() {
1774        assert_eq!(Ieee32::with_float(0.0).to_string(), "0.0");
1775        assert_eq!(Ieee32::with_float(-0.0).to_string(), "-0.0");
1776        assert_eq!(Ieee32::with_float(1.0).to_string(), "0x1.000000p0");
1777        assert_eq!(Ieee32::with_float(1.5).to_string(), "0x1.800000p0");
1778        assert_eq!(Ieee32::with_float(0.5).to_string(), "0x1.000000p-1");
1779        assert_eq!(
1780            Ieee32::with_float(f32::EPSILON).to_string(),
1781            "0x1.000000p-23"
1782        );
1783        assert_eq!(Ieee32::with_float(f32::MIN).to_string(), "-0x1.fffffep127");
1784        assert_eq!(Ieee32::with_float(f32::MAX).to_string(), "0x1.fffffep127");
1785        // Smallest positive normal number.
1786        assert_eq!(
1787            Ieee32::with_float(f32::MIN_POSITIVE).to_string(),
1788            "0x1.000000p-126"
1789        );
1790        // Subnormals.
1791        assert_eq!(
1792            Ieee32::with_float(f32::MIN_POSITIVE / 2.0).to_string(),
1793            "0x0.800000p-126"
1794        );
1795        assert_eq!(
1796            Ieee32::with_float(f32::MIN_POSITIVE * f32::EPSILON).to_string(),
1797            "0x0.000002p-126"
1798        );
1799        assert_eq!(Ieee32::with_float(f32::INFINITY).to_string(), "+Inf");
1800        assert_eq!(Ieee32::with_float(f32::NEG_INFINITY).to_string(), "-Inf");
1801        assert_eq!(Ieee32::with_float(f32::NAN).to_string(), "+NaN");
1802        assert_eq!(Ieee32::with_float(-f32::NAN).to_string(), "-NaN");
1803        // Construct some qNaNs with payloads.
1804        assert_eq!(Ieee32::with_bits(0x7fc00001).to_string(), "+NaN:0x1");
1805        assert_eq!(Ieee32::with_bits(0x7ff00001).to_string(), "+NaN:0x300001");
1806        // Signaling NaNs.
1807        assert_eq!(Ieee32::with_bits(0x7f800001).to_string(), "+sNaN:0x1");
1808        assert_eq!(Ieee32::with_bits(0x7fa00001).to_string(), "+sNaN:0x200001");
1809    }
1810
1811    #[test]
1812    fn parse_ieee32() {
1813        parse_ok::<Ieee32>("0.0", "0.0");
1814        parse_ok::<Ieee32>("+0.0", "0.0");
1815        parse_ok::<Ieee32>("-0.0", "-0.0");
1816        parse_ok::<Ieee32>("0x0", "0.0");
1817        parse_ok::<Ieee32>("0x0.0", "0.0");
1818        parse_ok::<Ieee32>("0x.0", "0.0");
1819        parse_ok::<Ieee32>("0x0.", "0.0");
1820        parse_ok::<Ieee32>("0x1", "0x1.000000p0");
1821        parse_ok::<Ieee32>("+0x1", "0x1.000000p0");
1822        parse_ok::<Ieee32>("-0x1", "-0x1.000000p0");
1823        parse_ok::<Ieee32>("0x10", "0x1.000000p4");
1824        parse_ok::<Ieee32>("0x10.0", "0x1.000000p4");
1825        parse_err::<Ieee32>("0.", "Float must be hexadecimal");
1826        parse_err::<Ieee32>(".0", "Float must be hexadecimal");
1827        parse_err::<Ieee32>("0", "Float must be hexadecimal");
1828        parse_err::<Ieee32>("-0", "Float must be hexadecimal");
1829        parse_err::<Ieee32>(".", "Float must be hexadecimal");
1830        parse_err::<Ieee32>("", "Float must be hexadecimal");
1831        parse_err::<Ieee32>("-", "Float must be hexadecimal");
1832        parse_err::<Ieee32>("0x", "No digits");
1833        parse_err::<Ieee32>("0x..", "Multiple radix points");
1834
1835        // Check significant bits.
1836        parse_ok::<Ieee32>("0x0.ffffff", "0x1.fffffep-1");
1837        parse_ok::<Ieee32>("0x1.fffffe", "0x1.fffffep0");
1838        parse_ok::<Ieee32>("0x3.fffffc", "0x1.fffffep1");
1839        parse_ok::<Ieee32>("0x7.fffff8", "0x1.fffffep2");
1840        parse_ok::<Ieee32>("0xf.fffff0", "0x1.fffffep3");
1841        parse_err::<Ieee32>("0x1.ffffff", "Too many significant bits");
1842        parse_err::<Ieee32>("0x1.fffffe00000000000000000000000000", "Too many digits");
1843
1844        // Exponents.
1845        parse_ok::<Ieee32>("0x1p3", "0x1.000000p3");
1846        parse_ok::<Ieee32>("0x1p-3", "0x1.000000p-3");
1847        parse_ok::<Ieee32>("0x1.0p3", "0x1.000000p3");
1848        parse_ok::<Ieee32>("0x2.0p3", "0x1.000000p4");
1849        parse_ok::<Ieee32>("0x1.0p127", "0x1.000000p127");
1850        parse_ok::<Ieee32>("0x1.0p-126", "0x1.000000p-126");
1851        parse_ok::<Ieee32>("0x0.1p-122", "0x1.000000p-126");
1852        parse_err::<Ieee32>("0x2.0p127", "Magnitude too large");
1853
1854        // Subnormals.
1855        parse_ok::<Ieee32>("0x1.0p-127", "0x0.800000p-126");
1856        parse_ok::<Ieee32>("0x1.0p-149", "0x0.000002p-126");
1857        parse_ok::<Ieee32>("0x0.000002p-126", "0x0.000002p-126");
1858        parse_err::<Ieee32>("0x0.100001p-126", "Subnormal underflow");
1859        parse_err::<Ieee32>("0x1.8p-149", "Subnormal underflow");
1860        parse_err::<Ieee32>("0x1.0p-150", "Magnitude too small");
1861
1862        // NaNs and Infs.
1863        parse_ok::<Ieee32>("Inf", "+Inf");
1864        parse_ok::<Ieee32>("+Inf", "+Inf");
1865        parse_ok::<Ieee32>("-Inf", "-Inf");
1866        parse_ok::<Ieee32>("NaN", "+NaN");
1867        parse_ok::<Ieee32>("+NaN", "+NaN");
1868        parse_ok::<Ieee32>("-NaN", "-NaN");
1869        parse_ok::<Ieee32>("NaN:0x0", "+NaN");
1870        parse_err::<Ieee32>("NaN:", "Float must be hexadecimal");
1871        parse_err::<Ieee32>("NaN:0", "Float must be hexadecimal");
1872        parse_err::<Ieee32>("NaN:0x", "Invalid NaN payload");
1873        parse_ok::<Ieee32>("NaN:0x000001", "+NaN:0x1");
1874        parse_ok::<Ieee32>("NaN:0x300001", "+NaN:0x300001");
1875        parse_err::<Ieee32>("NaN:0x400001", "Invalid NaN payload");
1876        parse_ok::<Ieee32>("sNaN:0x1", "+sNaN:0x1");
1877        parse_err::<Ieee32>("sNaN:0x0", "Invalid sNaN payload");
1878        parse_ok::<Ieee32>("sNaN:0x200001", "+sNaN:0x200001");
1879        parse_err::<Ieee32>("sNaN:0x400001", "Invalid sNaN payload");
1880    }
1881
1882    #[test]
1883    fn pow2_ieee32() {
1884        assert_eq!(Ieee32::pow2(0).to_string(), "0x1.000000p0");
1885        assert_eq!(Ieee32::pow2(1).to_string(), "0x1.000000p1");
1886        assert_eq!(Ieee32::pow2(-1).to_string(), "0x1.000000p-1");
1887        assert_eq!(Ieee32::pow2(127).to_string(), "0x1.000000p127");
1888        assert_eq!(Ieee32::pow2(-126).to_string(), "0x1.000000p-126");
1889
1890        assert_eq!((-Ieee32::pow2(1)).to_string(), "-0x1.000000p1");
1891    }
1892
1893    #[test]
1894    fn fcvt_to_sint_negative_overflow_ieee32() {
1895        for n in [8, 16] {
1896            assert_eq!(
1897                -((1u32 << (n - 1)) as f32) - 1.0,
1898                Ieee32::fcvt_to_sint_negative_overflow(n).as_f32(),
1899                "n = {n}"
1900            );
1901        }
1902    }
1903
1904    #[test]
1905    fn format_ieee64() {
1906        assert_eq!(Ieee64::with_float(0.0).to_string(), "0.0");
1907        assert_eq!(Ieee64::with_float(-0.0).to_string(), "-0.0");
1908        assert_eq!(Ieee64::with_float(1.0).to_string(), "0x1.0000000000000p0");
1909        assert_eq!(Ieee64::with_float(1.5).to_string(), "0x1.8000000000000p0");
1910        assert_eq!(Ieee64::with_float(0.5).to_string(), "0x1.0000000000000p-1");
1911        assert_eq!(
1912            Ieee64::with_float(f64::EPSILON).to_string(),
1913            "0x1.0000000000000p-52"
1914        );
1915        assert_eq!(
1916            Ieee64::with_float(f64::MIN).to_string(),
1917            "-0x1.fffffffffffffp1023"
1918        );
1919        assert_eq!(
1920            Ieee64::with_float(f64::MAX).to_string(),
1921            "0x1.fffffffffffffp1023"
1922        );
1923        // Smallest positive normal number.
1924        assert_eq!(
1925            Ieee64::with_float(f64::MIN_POSITIVE).to_string(),
1926            "0x1.0000000000000p-1022"
1927        );
1928        // Subnormals.
1929        assert_eq!(
1930            Ieee64::with_float(f64::MIN_POSITIVE / 2.0).to_string(),
1931            "0x0.8000000000000p-1022"
1932        );
1933        assert_eq!(
1934            Ieee64::with_float(f64::MIN_POSITIVE * f64::EPSILON).to_string(),
1935            "0x0.0000000000001p-1022"
1936        );
1937        assert_eq!(Ieee64::with_float(f64::INFINITY).to_string(), "+Inf");
1938        assert_eq!(Ieee64::with_float(f64::NEG_INFINITY).to_string(), "-Inf");
1939        assert_eq!(Ieee64::with_float(f64::NAN).to_string(), "+NaN");
1940        assert_eq!(Ieee64::with_float(-f64::NAN).to_string(), "-NaN");
1941        // Construct some qNaNs with payloads.
1942        assert_eq!(
1943            Ieee64::with_bits(0x7ff8000000000001).to_string(),
1944            "+NaN:0x1"
1945        );
1946        assert_eq!(
1947            Ieee64::with_bits(0x7ffc000000000001).to_string(),
1948            "+NaN:0x4000000000001"
1949        );
1950        // Signaling NaNs.
1951        assert_eq!(
1952            Ieee64::with_bits(0x7ff0000000000001).to_string(),
1953            "+sNaN:0x1"
1954        );
1955        assert_eq!(
1956            Ieee64::with_bits(0x7ff4000000000001).to_string(),
1957            "+sNaN:0x4000000000001"
1958        );
1959    }
1960
1961    #[test]
1962    fn parse_ieee64() {
1963        parse_ok::<Ieee64>("0.0", "0.0");
1964        parse_ok::<Ieee64>("-0.0", "-0.0");
1965        parse_ok::<Ieee64>("0x0", "0.0");
1966        parse_ok::<Ieee64>("0x0.0", "0.0");
1967        parse_ok::<Ieee64>("0x.0", "0.0");
1968        parse_ok::<Ieee64>("0x0.", "0.0");
1969        parse_ok::<Ieee64>("0x1", "0x1.0000000000000p0");
1970        parse_ok::<Ieee64>("-0x1", "-0x1.0000000000000p0");
1971        parse_ok::<Ieee64>("0x10", "0x1.0000000000000p4");
1972        parse_ok::<Ieee64>("0x10.0", "0x1.0000000000000p4");
1973        parse_err::<Ieee64>("0.", "Float must be hexadecimal");
1974        parse_err::<Ieee64>(".0", "Float must be hexadecimal");
1975        parse_err::<Ieee64>("0", "Float must be hexadecimal");
1976        parse_err::<Ieee64>("-0", "Float must be hexadecimal");
1977        parse_err::<Ieee64>(".", "Float must be hexadecimal");
1978        parse_err::<Ieee64>("", "Float must be hexadecimal");
1979        parse_err::<Ieee64>("-", "Float must be hexadecimal");
1980        parse_err::<Ieee64>("0x", "No digits");
1981        parse_err::<Ieee64>("0x..", "Multiple radix points");
1982
1983        // Check significant bits.
1984        parse_ok::<Ieee64>("0x0.fffffffffffff8", "0x1.fffffffffffffp-1");
1985        parse_ok::<Ieee64>("0x1.fffffffffffff", "0x1.fffffffffffffp0");
1986        parse_ok::<Ieee64>("0x3.ffffffffffffe", "0x1.fffffffffffffp1");
1987        parse_ok::<Ieee64>("0x7.ffffffffffffc", "0x1.fffffffffffffp2");
1988        parse_ok::<Ieee64>("0xf.ffffffffffff8", "0x1.fffffffffffffp3");
1989        parse_err::<Ieee64>("0x3.fffffffffffff", "Too many significant bits");
1990        parse_err::<Ieee64>("0x001.fffffe000000000000000000000000", "Too many digits");
1991
1992        // Exponents.
1993        parse_ok::<Ieee64>("0x1p3", "0x1.0000000000000p3");
1994        parse_ok::<Ieee64>("0x1p-3", "0x1.0000000000000p-3");
1995        parse_ok::<Ieee64>("0x1.0p3", "0x1.0000000000000p3");
1996        parse_ok::<Ieee64>("0x2.0p3", "0x1.0000000000000p4");
1997        parse_ok::<Ieee64>("0x1.0p1023", "0x1.0000000000000p1023");
1998        parse_ok::<Ieee64>("0x1.0p-1022", "0x1.0000000000000p-1022");
1999        parse_ok::<Ieee64>("0x0.1p-1018", "0x1.0000000000000p-1022");
2000        parse_err::<Ieee64>("0x2.0p1023", "Magnitude too large");
2001
2002        // Subnormals.
2003        parse_ok::<Ieee64>("0x1.0p-1023", "0x0.8000000000000p-1022");
2004        parse_ok::<Ieee64>("0x1.0p-1074", "0x0.0000000000001p-1022");
2005        parse_ok::<Ieee64>("0x0.0000000000001p-1022", "0x0.0000000000001p-1022");
2006        parse_err::<Ieee64>("0x0.10000000000008p-1022", "Subnormal underflow");
2007        parse_err::<Ieee64>("0x1.8p-1074", "Subnormal underflow");
2008        parse_err::<Ieee64>("0x1.0p-1075", "Magnitude too small");
2009
2010        // NaNs and Infs.
2011        parse_ok::<Ieee64>("Inf", "+Inf");
2012        parse_ok::<Ieee64>("-Inf", "-Inf");
2013        parse_ok::<Ieee64>("NaN", "+NaN");
2014        parse_ok::<Ieee64>("-NaN", "-NaN");
2015        parse_ok::<Ieee64>("NaN:0x0", "+NaN");
2016        parse_err::<Ieee64>("NaN:", "Float must be hexadecimal");
2017        parse_err::<Ieee64>("NaN:0", "Float must be hexadecimal");
2018        parse_err::<Ieee64>("NaN:0x", "Invalid NaN payload");
2019        parse_ok::<Ieee64>("NaN:0x000001", "+NaN:0x1");
2020        parse_ok::<Ieee64>("NaN:0x4000000000001", "+NaN:0x4000000000001");
2021        parse_err::<Ieee64>("NaN:0x8000000000001", "Invalid NaN payload");
2022        parse_ok::<Ieee64>("sNaN:0x1", "+sNaN:0x1");
2023        parse_err::<Ieee64>("sNaN:0x0", "Invalid sNaN payload");
2024        parse_ok::<Ieee64>("sNaN:0x4000000000001", "+sNaN:0x4000000000001");
2025        parse_err::<Ieee64>("sNaN:0x8000000000001", "Invalid sNaN payload");
2026    }
2027
2028    #[test]
2029    fn pow2_ieee64() {
2030        assert_eq!(Ieee64::pow2(0).to_string(), "0x1.0000000000000p0");
2031        assert_eq!(Ieee64::pow2(1).to_string(), "0x1.0000000000000p1");
2032        assert_eq!(Ieee64::pow2(-1).to_string(), "0x1.0000000000000p-1");
2033        assert_eq!(Ieee64::pow2(1023).to_string(), "0x1.0000000000000p1023");
2034        assert_eq!(Ieee64::pow2(-1022).to_string(), "0x1.0000000000000p-1022");
2035
2036        assert_eq!((-Ieee64::pow2(1)).to_string(), "-0x1.0000000000000p1");
2037    }
2038
2039    #[test]
2040    fn fcvt_to_sint_negative_overflow_ieee64() {
2041        for n in [8, 16, 32] {
2042            assert_eq!(
2043                -((1u64 << (n - 1)) as f64) - 1.0,
2044                Ieee64::fcvt_to_sint_negative_overflow(n).as_f64(),
2045                "n = {n}"
2046            );
2047        }
2048    }
2049
2050    #[test]
2051    fn format_ieee128() {
2052        assert_eq!(
2053            Ieee128::with_bits(0x00000000000000000000000000000000).to_string(), // 0.0
2054            "0.0"
2055        );
2056        assert_eq!(
2057            Ieee128::with_bits(0x80000000000000000000000000000000).to_string(), // -0.0
2058            "-0.0"
2059        );
2060        assert_eq!(
2061            Ieee128::with_bits(0x3fff0000000000000000000000000000).to_string(), // 1.0
2062            "0x1.0000000000000000000000000000p0"
2063        );
2064        assert_eq!(
2065            Ieee128::with_bits(0x3fff8000000000000000000000000000).to_string(), // 1.5
2066            "0x1.8000000000000000000000000000p0"
2067        );
2068        assert_eq!(
2069            Ieee128::with_bits(0x3ffe0000000000000000000000000000).to_string(), // 0.5
2070            "0x1.0000000000000000000000000000p-1"
2071        );
2072        assert_eq!(
2073            Ieee128::with_bits(0x3f8f0000000000000000000000000000).to_string(), // `f128::EPSILON`
2074            "0x1.0000000000000000000000000000p-112"
2075        );
2076        assert_eq!(
2077            Ieee128::with_bits(0xfffeffffffffffffffffffffffffffff).to_string(), // `f128::MIN`
2078            "-0x1.ffffffffffffffffffffffffffffp16383"
2079        );
2080        assert_eq!(
2081            Ieee128::with_bits(0x7ffeffffffffffffffffffffffffffff).to_string(), // `f128::MAX`
2082            "0x1.ffffffffffffffffffffffffffffp16383"
2083        );
2084        // Smallest positive normal number.
2085        assert_eq!(
2086            Ieee128::with_bits(0x00010000000000000000000000000000).to_string(), // `f128::MIN_POSITIVE`
2087            "0x1.0000000000000000000000000000p-16382"
2088        );
2089        // Subnormals.
2090        assert_eq!(
2091            Ieee128::with_bits(0x00008000000000000000000000000000).to_string(), // `f128::MIN_POSITIVE / 2.0`
2092            "0x0.8000000000000000000000000000p-16382"
2093        );
2094        assert_eq!(
2095            Ieee128::with_bits(0x00000000000000000000000000000001).to_string(), // `f128::MIN_POSITIVE * f128::EPSILON`
2096            "0x0.0000000000000000000000000001p-16382"
2097        );
2098        assert_eq!(
2099            Ieee128::with_bits(0x7fff0000000000000000000000000000).to_string(), // `f128::INFINITY`
2100            "+Inf"
2101        );
2102        assert_eq!(
2103            Ieee128::with_bits(0xffff0000000000000000000000000000).to_string(), // `f128::NEG_INFINITY`
2104            "-Inf"
2105        );
2106        assert_eq!(
2107            Ieee128::with_bits(0x7fff8000000000000000000000000000).to_string(), // `f128::NAN`
2108            "+NaN"
2109        );
2110        assert_eq!(
2111            Ieee128::with_bits(0xffff8000000000000000000000000000).to_string(), // `-f128::NAN`
2112            "-NaN"
2113        );
2114        // Construct some qNaNs with payloads.
2115        assert_eq!(
2116            Ieee128::with_bits(0x7fff8000000000000000000000000001).to_string(),
2117            "+NaN:0x1"
2118        );
2119        assert_eq!(
2120            Ieee128::with_bits(0x7fffc000000000000000000000000001).to_string(),
2121            "+NaN:0x4000000000000000000000000001"
2122        );
2123        // Signaling NaNs.
2124        assert_eq!(
2125            Ieee128::with_bits(0x7fff0000000000000000000000000001).to_string(),
2126            "+sNaN:0x1"
2127        );
2128        assert_eq!(
2129            Ieee128::with_bits(0x7fff4000000000000000000000000001).to_string(),
2130            "+sNaN:0x4000000000000000000000000001"
2131        );
2132    }
2133
2134    #[test]
2135    fn parse_ieee128() {
2136        parse_ok::<Ieee128>("0.0", "0.0");
2137        parse_ok::<Ieee128>("-0.0", "-0.0");
2138        parse_ok::<Ieee128>("0x0", "0.0");
2139        parse_ok::<Ieee128>("0x0.0", "0.0");
2140        parse_ok::<Ieee128>("0x.0", "0.0");
2141        parse_ok::<Ieee128>("0x0.", "0.0");
2142        parse_ok::<Ieee128>("0x1", "0x1.0000000000000000000000000000p0");
2143        parse_ok::<Ieee128>("-0x1", "-0x1.0000000000000000000000000000p0");
2144        parse_ok::<Ieee128>("0x10", "0x1.0000000000000000000000000000p4");
2145        parse_ok::<Ieee128>("0x10.0", "0x1.0000000000000000000000000000p4");
2146        parse_err::<Ieee128>("0.", "Float must be hexadecimal");
2147        parse_err::<Ieee128>(".0", "Float must be hexadecimal");
2148        parse_err::<Ieee128>("0", "Float must be hexadecimal");
2149        parse_err::<Ieee128>("-0", "Float must be hexadecimal");
2150        parse_err::<Ieee128>(".", "Float must be hexadecimal");
2151        parse_err::<Ieee128>("", "Float must be hexadecimal");
2152        parse_err::<Ieee128>("-", "Float must be hexadecimal");
2153        parse_err::<Ieee128>("0x", "No digits");
2154        parse_err::<Ieee128>("0x..", "Multiple radix points");
2155
2156        // Check significant bits.
2157        parse_ok::<Ieee128>(
2158            "0x0.ffffffffffffffffffffffffffff8",
2159            "0x1.ffffffffffffffffffffffffffffp-1",
2160        );
2161        parse_ok::<Ieee128>(
2162            "0x1.ffffffffffffffffffffffffffff",
2163            "0x1.ffffffffffffffffffffffffffffp0",
2164        );
2165        parse_ok::<Ieee128>(
2166            "0x3.fffffffffffffffffffffffffffe",
2167            "0x1.ffffffffffffffffffffffffffffp1",
2168        );
2169        parse_ok::<Ieee128>(
2170            "0x7.fffffffffffffffffffffffffffc",
2171            "0x1.ffffffffffffffffffffffffffffp2",
2172        );
2173        parse_ok::<Ieee128>(
2174            "0xf.fffffffffffffffffffffffffff8",
2175            "0x1.ffffffffffffffffffffffffffffp3",
2176        );
2177        parse_err::<Ieee128>(
2178            "0x3.ffffffffffffffffffffffffffff",
2179            "Too many significant bits",
2180        );
2181        parse_err::<Ieee128>("0x001.fffffe000000000000000000000000", "Too many digits");
2182
2183        // Exponents.
2184        parse_ok::<Ieee128>("0x1p3", "0x1.0000000000000000000000000000p3");
2185        parse_ok::<Ieee128>("0x1p-3", "0x1.0000000000000000000000000000p-3");
2186        parse_ok::<Ieee128>("0x1.0p3", "0x1.0000000000000000000000000000p3");
2187        parse_ok::<Ieee128>("0x2.0p3", "0x1.0000000000000000000000000000p4");
2188        parse_ok::<Ieee128>("0x1.0p16383", "0x1.0000000000000000000000000000p16383");
2189        parse_ok::<Ieee128>("0x1.0p-16382", "0x1.0000000000000000000000000000p-16382");
2190        parse_ok::<Ieee128>("0x0.1p-16378", "0x1.0000000000000000000000000000p-16382");
2191        parse_err::<Ieee128>("0x2.0p16383", "Magnitude too large");
2192
2193        // Subnormals.
2194        parse_ok::<Ieee128>("0x1.0p-16383", "0x0.8000000000000000000000000000p-16382");
2195        parse_ok::<Ieee128>("0x1.0p-16494", "0x0.0000000000000000000000000001p-16382");
2196        parse_ok::<Ieee128>(
2197            "0x0.0000000000000000000000000001p-16382",
2198            "0x0.0000000000000000000000000001p-16382",
2199        );
2200        parse_err::<Ieee128>(
2201            "0x0.10000000000000000000000000008p-16382",
2202            "Subnormal underflow",
2203        );
2204        parse_err::<Ieee128>("0x1.8p-16494", "Subnormal underflow");
2205        parse_err::<Ieee128>("0x1.0p-16495", "Magnitude too small");
2206
2207        // NaNs and Infs.
2208        parse_ok::<Ieee128>("Inf", "+Inf");
2209        parse_ok::<Ieee128>("-Inf", "-Inf");
2210        parse_ok::<Ieee128>("NaN", "+NaN");
2211        parse_ok::<Ieee128>("-NaN", "-NaN");
2212        parse_ok::<Ieee128>("NaN:0x0", "+NaN");
2213        parse_err::<Ieee128>("NaN:", "Float must be hexadecimal");
2214        parse_err::<Ieee128>("NaN:0", "Float must be hexadecimal");
2215        parse_err::<Ieee128>("NaN:0x", "Invalid NaN payload");
2216        parse_ok::<Ieee128>("NaN:0x000001", "+NaN:0x1");
2217        parse_ok::<Ieee128>(
2218            "NaN:0x4000000000000000000000000001",
2219            "+NaN:0x4000000000000000000000000001",
2220        );
2221        parse_err::<Ieee128>("NaN:0x8000000000000000000000000001", "Invalid NaN payload");
2222        parse_ok::<Ieee128>("sNaN:0x1", "+sNaN:0x1");
2223        parse_err::<Ieee128>("sNaN:0x0", "Invalid sNaN payload");
2224        parse_ok::<Ieee128>(
2225            "sNaN:0x4000000000000000000000000001",
2226            "+sNaN:0x4000000000000000000000000001",
2227        );
2228        parse_err::<Ieee128>(
2229            "sNaN:0x8000000000000000000000000001",
2230            "Invalid sNaN payload",
2231        );
2232    }
2233
2234    #[test]
2235    fn pow2_ieee128() {
2236        assert_eq!(
2237            Ieee128::pow2(0).to_string(),
2238            "0x1.0000000000000000000000000000p0"
2239        );
2240        assert_eq!(
2241            Ieee128::pow2(1).to_string(),
2242            "0x1.0000000000000000000000000000p1"
2243        );
2244        assert_eq!(
2245            Ieee128::pow2(-1).to_string(),
2246            "0x1.0000000000000000000000000000p-1"
2247        );
2248        assert_eq!(
2249            Ieee128::pow2(16383).to_string(),
2250            "0x1.0000000000000000000000000000p16383"
2251        );
2252        assert_eq!(
2253            Ieee128::pow2(-16382).to_string(),
2254            "0x1.0000000000000000000000000000p-16382"
2255        );
2256
2257        assert_eq!(
2258            (-Ieee128::pow2(1)).to_string(),
2259            "-0x1.0000000000000000000000000000p1"
2260        );
2261    }
2262
2263    #[test]
2264    fn partial_cmp_zero_ieee128() {
2265        let zero = Ieee128::with_bits(0);
2266        let one = Ieee128::pow2(0);
2267        let subnormal = Ieee128::with_bits(1);
2268
2269        for zero in [zero, -zero] {
2270            assert_eq!(zero.partial_cmp(&zero), Some(Ordering::Equal));
2271            assert_eq!(zero.partial_cmp(&-zero), Some(Ordering::Equal));
2272
2273            for (value, ordering) in [
2274                (one, Ordering::Greater),
2275                (-one, Ordering::Less),
2276                (subnormal, Ordering::Greater),
2277                (-subnormal, Ordering::Less),
2278            ] {
2279                assert_eq!(
2280                    value.partial_cmp(&zero),
2281                    Some(ordering),
2282                    "{value} compared with {zero}"
2283                );
2284                assert_eq!(
2285                    zero.partial_cmp(&value),
2286                    Some(ordering.reverse()),
2287                    "{zero} compared with {value}"
2288                );
2289            }
2290        }
2291    }
2292
2293    #[test]
2294    fn fcvt_to_sint_negative_overflow_ieee128() {
2295        // FIXME(#8312): Replace with commented out version once Rust f128 support is stabilised.
2296        // for n in [8, 16, 32, 64] {
2297        //     assert_eq!(
2298        //         -((1u128 << (n - 1)) as f128) - 1.0,
2299        //         Ieee128::fcvt_to_sint_negative_overflow(n).as_f128(),
2300        //         "n = {n}"
2301        //     );
2302        // }
2303        for (n, expected) in [
2304            (8, "-0x1.0200000000000000000000000000p7"),
2305            (16, "-0x1.0002000000000000000000000000p15"),
2306            (32, "-0x1.0000000200000000000000000000p31"),
2307            (64, "-0x1.0000000000000002000000000000p63"),
2308        ] {
2309            assert_eq!(
2310                expected,
2311                Ieee128::fcvt_to_sint_negative_overflow(n).to_string(),
2312                "n = {n}"
2313            );
2314        }
2315    }
2316}