compiler_builtins/libm/src/math/support/
hex_float.rs

1//! Utilities for working with hex float formats.
2
3use super::{Round, Status, f32_from_bits, f64_from_bits};
4
5/// Construct a 16-bit float from hex float representation (C-style)
6#[cfg(f16_enabled)]
7pub const fn hf16(s: &str) -> f16 {
8    match parse_hex_exact(s, 16, 10) {
9        Ok(bits) => f16::from_bits(bits as u16),
10        Err(HexFloatParseError(s)) => panic!("{}", s),
11    }
12}
13
14/// Construct a 32-bit float from hex float representation (C-style)
15#[allow(unused)]
16pub const fn hf32(s: &str) -> f32 {
17    match parse_hex_exact(s, 32, 23) {
18        Ok(bits) => f32_from_bits(bits as u32),
19        Err(HexFloatParseError(s)) => panic!("{}", s),
20    }
21}
22
23/// Construct a 64-bit float from hex float representation (C-style)
24pub const fn hf64(s: &str) -> f64 {
25    match parse_hex_exact(s, 64, 52) {
26        Ok(bits) => f64_from_bits(bits as u64),
27        Err(HexFloatParseError(s)) => panic!("{}", s),
28    }
29}
30
31/// Construct a 128-bit float from hex float representation (C-style)
32#[cfg(f128_enabled)]
33pub const fn hf128(s: &str) -> f128 {
34    match parse_hex_exact(s, 128, 112) {
35        Ok(bits) => f128::from_bits(bits),
36        Err(HexFloatParseError(s)) => panic!("{}", s),
37    }
38}
39#[derive(Copy, Clone, Debug)]
40pub struct HexFloatParseError(&'static str);
41
42/// Parses any float to its bitwise representation, returning an error if it cannot be represented exactly
43pub const fn parse_hex_exact(
44    s: &str,
45    bits: u32,
46    sig_bits: u32,
47) -> Result<u128, HexFloatParseError> {
48    match parse_any(s, bits, sig_bits, Round::Nearest) {
49        Err(e) => Err(e),
50        Ok((bits, Status::OK)) => Ok(bits),
51        Ok((_, status)) if status.overflow() => Err(HexFloatParseError("the value is too huge")),
52        Ok((_, status)) if status.underflow() => Err(HexFloatParseError("the value is too tiny")),
53        Ok((_, status)) if status.inexact() => Err(HexFloatParseError("the value is too precise")),
54        Ok(_) => unreachable!(),
55    }
56}
57
58/// Parse any float from hex to its bitwise representation.
59pub const fn parse_any(
60    s: &str,
61    bits: u32,
62    sig_bits: u32,
63    round: Round,
64) -> Result<(u128, Status), HexFloatParseError> {
65    let mut b = s.as_bytes();
66
67    if sig_bits > 119 || bits > 128 || bits < sig_bits + 3 || bits > sig_bits + 30 {
68        return Err(HexFloatParseError("unsupported target float configuration"));
69    }
70
71    let neg = matches!(b, [b'-', ..]);
72    if let &[b'-' | b'+', ref rest @ ..] = b {
73        b = rest;
74    }
75
76    let sign_bit = 1 << (bits - 1);
77    let quiet_bit = 1 << (sig_bits - 1);
78    let nan = sign_bit - quiet_bit;
79    let inf = nan - quiet_bit;
80
81    let (mut x, status) = match *b {
82        [b'i' | b'I', b'n' | b'N', b'f' | b'F'] => (inf, Status::OK),
83        [b'n' | b'N', b'a' | b'A', b'n' | b'N'] => (nan, Status::OK),
84        [b'0', b'x' | b'X', ref rest @ ..] => {
85            let round = match (neg, round) {
86                // parse("-x", Round::Positive) == -parse("x", Round::Negative)
87                (true, Round::Positive) => Round::Negative,
88                (true, Round::Negative) => Round::Positive,
89                // rounding toward nearest or zero are symmetric
90                (true, Round::Nearest | Round::Zero) | (false, _) => round,
91            };
92            match parse_finite(rest, bits, sig_bits, round) {
93                Err(e) => return Err(e),
94                Ok(res) => res,
95            }
96        }
97        _ => return Err(HexFloatParseError("no hex indicator")),
98    };
99
100    if neg {
101        x ^= sign_bit;
102    }
103
104    Ok((x, status))
105}
106
107const fn parse_finite(
108    b: &[u8],
109    bits: u32,
110    sig_bits: u32,
111    rounding_mode: Round,
112) -> Result<(u128, Status), HexFloatParseError> {
113    let exp_bits: u32 = bits - sig_bits - 1;
114    let max_msb: i32 = (1 << (exp_bits - 1)) - 1;
115    // The exponent of one ULP in the subnormals
116    let min_lsb: i32 = 1 - max_msb - sig_bits as i32;
117
118    let (mut sig, mut exp) = match parse_hex(b) {
119        Err(e) => return Err(e),
120        Ok(Parsed { sig: 0, .. }) => return Ok((0, Status::OK)),
121        Ok(Parsed { sig, exp }) => (sig, exp),
122    };
123
124    let mut round_bits = u128_ilog2(sig) as i32 - sig_bits as i32;
125
126    // Round at least up to min_lsb
127    if exp < min_lsb - round_bits {
128        round_bits = min_lsb - exp;
129    }
130
131    let mut status = Status::OK;
132
133    exp += round_bits;
134
135    if round_bits > 0 {
136        // first, prepare for rounding exactly two bits
137        if round_bits == 1 {
138            sig <<= 1;
139        } else if round_bits > 2 {
140            sig = shr_odd_rounding(sig, (round_bits - 2) as u32);
141        }
142
143        if sig & 0b11 != 0 {
144            status = Status::INEXACT;
145        }
146
147        sig = shr2_round(sig, rounding_mode);
148    } else if round_bits < 0 {
149        sig <<= -round_bits;
150    }
151
152    // The parsed value is X = sig * 2^exp
153    // Expressed as a multiple U of the smallest subnormal value:
154    // X = U * 2^min_lsb, so U = sig * 2^(exp-min_lsb)
155    let uexp = (exp - min_lsb) as u128;
156    let uexp = uexp << sig_bits;
157
158    // Note that it is possible for the exponent bits to equal 2 here
159    // if the value rounded up, but that means the mantissa is all zeroes
160    // so the value is still correct
161    debug_assert!(sig <= 2 << sig_bits);
162
163    let inf = ((1 << exp_bits) - 1) << sig_bits;
164
165    let bits = match sig.checked_add(uexp) {
166        Some(bits) if bits < inf => {
167            // inexact subnormal or zero?
168            if status.inexact() && bits < (1 << sig_bits) {
169                status = status.with(Status::UNDERFLOW);
170            }
171            bits
172        }
173        _ => {
174            // overflow to infinity
175            status = status.with(Status::OVERFLOW).with(Status::INEXACT);
176            match rounding_mode {
177                Round::Positive | Round::Nearest => inf,
178                Round::Negative | Round::Zero => inf - 1,
179            }
180        }
181    };
182    Ok((bits, status))
183}
184
185/// Shift right, rounding all inexact divisions to the nearest odd number
186/// E.g. (0 >> 4) -> 0, (1..=31 >> 4) -> 1, (32 >> 4) -> 2, ...
187///
188/// Useful for reducing a number before rounding the last two bits, since
189/// the result of the final rounding is preserved for all rounding modes.
190const fn shr_odd_rounding(x: u128, k: u32) -> u128 {
191    if k < 128 {
192        let inexact = x.trailing_zeros() < k;
193        (x >> k) | (inexact as u128)
194    } else {
195        (x != 0) as u128
196    }
197}
198
199/// Divide by 4, rounding with the given mode
200const fn shr2_round(mut x: u128, round: Round) -> u128 {
201    let t = (x as u32) & 0b111;
202    x >>= 2;
203    match round {
204        // Look-up-table on the last three bits for when to round up
205        Round::Nearest => x + ((0b11001000_u8 >> t) & 1) as u128,
206
207        Round::Negative => x,
208        Round::Zero => x,
209        Round::Positive => x + (t & 0b11 != 0) as u128,
210    }
211}
212
213/// A parsed finite and unsigned floating point number.
214struct Parsed {
215    /// Absolute value sig * 2^exp
216    sig: u128,
217    exp: i32,
218}
219
220/// Parse a hexadecimal float x
221const fn parse_hex(mut b: &[u8]) -> Result<Parsed, HexFloatParseError> {
222    let mut sig: u128 = 0;
223    let mut exp: i32 = 0;
224
225    let mut seen_point = false;
226    let mut some_digits = false;
227    let mut inexact = false;
228
229    while let &[c, ref rest @ ..] = b {
230        b = rest;
231
232        match c {
233            b'.' => {
234                if seen_point {
235                    return Err(HexFloatParseError(
236                        "unexpected '.' parsing fractional digits",
237                    ));
238                }
239                seen_point = true;
240                continue;
241            }
242            b'p' | b'P' => break,
243            c => {
244                let digit = match hex_digit(c) {
245                    Some(d) => d,
246                    None => return Err(HexFloatParseError("expected hexadecimal digit")),
247                };
248                some_digits = true;
249
250                if (sig >> 124) == 0 {
251                    sig <<= 4;
252                    sig |= digit as u128;
253                } else {
254                    // FIXME: it is technically possible for exp to overflow if parsing a string with >500M digits
255                    exp += 4;
256                    inexact |= digit != 0;
257                }
258                // Up until the fractional point, the value grows
259                // with more digits, but after it the exponent is
260                // compensated to match.
261                if seen_point {
262                    exp -= 4;
263                }
264            }
265        }
266    }
267    // If we've set inexact, the exact value has more than 125
268    // significant bits, and lies somewhere between sig and sig + 1.
269    // Because we'll round off at least two of the trailing bits,
270    // setting the last bit gives correct rounding for inexact values.
271    sig |= inexact as u128;
272
273    if !some_digits {
274        return Err(HexFloatParseError("at least one digit is required"));
275    };
276
277    some_digits = false;
278
279    let negate_exp = matches!(b, [b'-', ..]);
280    if let &[b'-' | b'+', ref rest @ ..] = b {
281        b = rest;
282    }
283
284    let mut pexp: u32 = 0;
285    while let &[c, ref rest @ ..] = b {
286        b = rest;
287        let digit = match dec_digit(c) {
288            Some(d) => d,
289            None => return Err(HexFloatParseError("expected decimal digit")),
290        };
291        some_digits = true;
292        pexp = pexp.saturating_mul(10);
293        pexp += digit as u32;
294    }
295
296    if !some_digits {
297        return Err(HexFloatParseError(
298            "at least one exponent digit is required",
299        ));
300    };
301
302    {
303        let e;
304        if negate_exp {
305            e = (exp as i64) - (pexp as i64);
306        } else {
307            e = (exp as i64) + (pexp as i64);
308        };
309
310        exp = if e < i32::MIN as i64 {
311            i32::MIN
312        } else if e > i32::MAX as i64 {
313            i32::MAX
314        } else {
315            e as i32
316        };
317    }
318    /* FIXME(msrv): once MSRV >= 1.66, replace the above workaround block with:
319    if negate_exp {
320        exp = exp.saturating_sub_unsigned(pexp);
321    } else {
322        exp = exp.saturating_add_unsigned(pexp);
323    };
324    */
325
326    Ok(Parsed { sig, exp })
327}
328
329const fn dec_digit(c: u8) -> Option<u8> {
330    match c {
331        b'0'..=b'9' => Some(c - b'0'),
332        _ => None,
333    }
334}
335
336const fn hex_digit(c: u8) -> Option<u8> {
337    match c {
338        b'0'..=b'9' => Some(c - b'0'),
339        b'a'..=b'f' => Some(c - b'a' + 10),
340        b'A'..=b'F' => Some(c - b'A' + 10),
341        _ => None,
342    }
343}
344
345/* FIXME(msrv): vendor some things that are not const stable at our MSRV */
346
347/// `u128::ilog2`
348const fn u128_ilog2(v: u128) -> u32 {
349    assert!(v != 0);
350    u128::BITS - 1 - v.leading_zeros()
351}
352
353#[cfg(any(test, feature = "unstable-public-internals"))]
354mod hex_fmt {
355    use core::fmt;
356
357    use crate::support::Float;
358
359    /// Format a floating point number as its IEEE hex (`%a`) representation.
360    pub struct Hexf<F>(pub F);
361
362    // Adapted from https://github.com/ericseppanen/hexfloat2/blob/a5c27932f0ff/src/format.rs
363    #[cfg(not(feature = "compiler-builtins"))]
364    pub(super) fn fmt_any_hex<F: Float>(x: &F, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365        if x.is_sign_negative() {
366            write!(f, "-")?;
367        }
368
369        if x.is_nan() {
370            return write!(f, "NaN");
371        } else if x.is_infinite() {
372            return write!(f, "inf");
373        } else if *x == F::ZERO {
374            return write!(f, "0x0p+0");
375        }
376
377        let mut exponent = x.exp_unbiased();
378        let sig = x.to_bits() & F::SIG_MASK;
379
380        let bias = F::EXP_BIAS as i32;
381        // The mantissa MSB needs to be shifted up to the nearest nibble.
382        let mshift = (4 - (F::SIG_BITS % 4)) % 4;
383        let sig = sig << mshift;
384        // The width is rounded up to the nearest char (4 bits)
385        let mwidth = (F::SIG_BITS as usize + 3) / 4;
386        let leading = if exponent == -bias {
387            // subnormal number means we shift our output by 1 bit.
388            exponent += 1;
389            "0."
390        } else {
391            "1."
392        };
393
394        write!(f, "0x{leading}{sig:0mwidth$x}p{exponent:+}")
395    }
396
397    #[cfg(feature = "compiler-builtins")]
398    pub(super) fn fmt_any_hex<F: Float>(_x: &F, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
399        unimplemented!()
400    }
401
402    impl<F: Float> fmt::LowerHex for Hexf<F> {
403        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404            cfg_if! {
405                if #[cfg(feature = "compiler-builtins")] {
406                    let _ = f;
407                    unimplemented!()
408                } else {
409                    fmt_any_hex(&self.0, f)
410                }
411            }
412        }
413    }
414
415    impl<F: Float> fmt::LowerHex for Hexf<(F, F)> {
416        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417            cfg_if! {
418                if #[cfg(feature = "compiler-builtins")] {
419                    let _ = f;
420                    unimplemented!()
421                } else {
422                    write!(f, "({:x}, {:x})", Hexf(self.0.0), Hexf(self.0.1))
423                }
424            }
425        }
426    }
427
428    impl<F: Float> fmt::LowerHex for Hexf<(F, i32)> {
429        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430            cfg_if! {
431                if #[cfg(feature = "compiler-builtins")] {
432                    let _ = f;
433                    unimplemented!()
434                } else {
435                    write!(f, "({:x}, {:x})", Hexf(self.0.0), Hexf(self.0.1))
436                }
437            }
438        }
439    }
440
441    impl fmt::LowerHex for Hexf<i32> {
442        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443            cfg_if! {
444                if #[cfg(feature = "compiler-builtins")] {
445                    let _ = f;
446                    unimplemented!()
447                } else {
448                    fmt::LowerHex::fmt(&self.0, f)
449                }
450            }
451        }
452    }
453
454    impl<T> fmt::Debug for Hexf<T>
455    where
456        Hexf<T>: fmt::LowerHex,
457    {
458        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459            cfg_if! {
460                if #[cfg(feature = "compiler-builtins")] {
461                    let _ = f;
462                    unimplemented!()
463                } else {
464                    fmt::LowerHex::fmt(self, f)
465                }
466            }
467        }
468    }
469
470    impl<T> fmt::Display for Hexf<T>
471    where
472        Hexf<T>: fmt::LowerHex,
473    {
474        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
475            cfg_if! {
476                if #[cfg(feature = "compiler-builtins")] {
477                    let _ = f;
478                    unimplemented!()
479                } else {
480                    fmt::LowerHex::fmt(self, f)
481                }
482            }
483        }
484    }
485}
486
487#[cfg(any(test, feature = "unstable-public-internals"))]
488pub use hex_fmt::*;
489
490#[cfg(test)]
491mod parse_tests {
492    extern crate std;
493    use std::{format, println};
494
495    use super::*;
496
497    #[cfg(f16_enabled)]
498    fn rounding_properties(s: &str) -> Result<(), HexFloatParseError> {
499        let (xd, s0) = parse_any(s, 16, 10, Round::Negative)?;
500        let (xu, s1) = parse_any(s, 16, 10, Round::Positive)?;
501        let (xz, s2) = parse_any(s, 16, 10, Round::Zero)?;
502        let (xn, s3) = parse_any(s, 16, 10, Round::Nearest)?;
503
504        // FIXME: A value between the least normal and largest subnormal
505        // could have underflow status depend on rounding mode.
506
507        if let Status::OK = s0 {
508            // an exact result is the same for all rounding modes
509            assert_eq!(s0, s1);
510            assert_eq!(s0, s2);
511            assert_eq!(s0, s3);
512
513            assert_eq!(xd, xu);
514            assert_eq!(xd, xz);
515            assert_eq!(xd, xn);
516        } else {
517            assert!([s0, s1, s2, s3].into_iter().all(Status::inexact));
518
519            let xd = f16::from_bits(xd as u16);
520            let xu = f16::from_bits(xu as u16);
521            let xz = f16::from_bits(xz as u16);
522            let xn = f16::from_bits(xn as u16);
523
524            assert_biteq!(xd.next_up(), xu, "s={s}, xd={xd:?}, xu={xu:?}");
525
526            let signs = [xd, xu, xz, xn].map(f16::is_sign_negative);
527
528            if signs == [true; 4] {
529                assert_biteq!(xz, xu);
530            } else {
531                assert_eq!(signs, [false; 4]);
532                assert_biteq!(xz, xd);
533            }
534
535            if xn.to_bits() != xd.to_bits() {
536                assert_biteq!(xn, xu);
537            }
538        }
539        Ok(())
540    }
541    #[test]
542    #[cfg(f16_enabled)]
543    fn test_rounding() {
544        let n = 1_i32 << 14;
545        for i in -n..n {
546            let u = i.rotate_right(11) as u32;
547            let s = format!("{}", Hexf(f32::from_bits(u)));
548            assert!(rounding_properties(&s).is_ok());
549        }
550    }
551
552    #[test]
553    fn test_parse_any() {
554        for k in -149..=127 {
555            let s = format!("0x1p{k}");
556            let x = hf32(&s);
557            let y = if k < 0 {
558                0.5f32.powi(-k)
559            } else {
560                2.0f32.powi(k)
561            };
562            assert_eq!(x, y);
563        }
564
565        let mut s = *b"0x.0000000p-121";
566        for e in 0..40 {
567            for k in 0..(1 << 15) {
568                let expected = f32::from_bits(k) * 2.0f32.powi(e);
569                let x = hf32(std::str::from_utf8(&s).unwrap());
570                assert_eq!(
571                    x.to_bits(),
572                    expected.to_bits(),
573                    "\
574                    e={e}\n\
575                    k={k}\n\
576                    x={x}\n\
577                    expected={expected}\n\
578                    s={}\n\
579                    f32::from_bits(k)={}\n\
580                    2.0f32.powi(e)={}\
581                    ",
582                    std::str::from_utf8(&s).unwrap(),
583                    f32::from_bits(k),
584                    2.0f32.powi(e),
585                );
586                for i in (3..10).rev() {
587                    if s[i] == b'f' {
588                        s[i] = b'0';
589                    } else if s[i] == b'9' {
590                        s[i] = b'a';
591                        break;
592                    } else {
593                        s[i] += 1;
594                        break;
595                    }
596                }
597            }
598            for i in (12..15).rev() {
599                if s[i] == b'0' {
600                    s[i] = b'9';
601                } else {
602                    s[i] -= 1;
603                    break;
604                }
605            }
606            for i in (3..10).rev() {
607                s[i] = b'0';
608            }
609        }
610    }
611
612    // FIXME: this test is causing failures that are likely UB on various platforms
613    #[cfg(all(target_arch = "x86_64", target_os = "linux"))]
614    #[test]
615    #[cfg(f128_enabled)]
616    fn rounding() {
617        let pi = std::f128::consts::PI;
618        let s = format!("{}", Hexf(pi));
619
620        for k in 0..=111 {
621            let (bits, status) = parse_any(&s, 128 - k, 112 - k, Round::Nearest).unwrap();
622            let scale = (1u128 << (112 - k - 1)) as f128;
623            let expected = (pi * scale).round_ties_even() / scale;
624            assert_eq!(bits << k, expected.to_bits(), "k = {k}, s = {s}");
625            assert_eq!(expected != pi, status.inexact());
626        }
627    }
628    #[test]
629    fn rounding_extreme_underflow() {
630        for k in 1..1000 {
631            let s = format!("0x1p{}", -149 - k);
632            let Ok((bits, status)) = parse_any(&s, 32, 23, Round::Nearest) else {
633                unreachable!()
634            };
635            assert_eq!(bits, 0, "{s} should round to zero, got bits={bits}");
636            assert!(
637                status.underflow(),
638                "should indicate underflow when parsing {s}"
639            );
640            assert!(status.inexact(), "should indicate inexact when parsing {s}");
641        }
642    }
643    #[test]
644    fn long_tail() {
645        for k in 1..1000 {
646            let s = format!("0x1.{}p0", "0".repeat(k));
647            let Ok(bits) = parse_hex_exact(&s, 32, 23) else {
648                panic!("parsing {s} failed")
649            };
650            assert_eq!(f32::from_bits(bits as u32), 1.0);
651
652            let s = format!("0x1.{}1p0", "0".repeat(k));
653            let Ok((bits, status)) = parse_any(&s, 32, 23, Round::Nearest) else {
654                unreachable!()
655            };
656            if status.inexact() {
657                assert!(1.0 == f32::from_bits(bits as u32));
658            } else {
659                assert!(1.0 < f32::from_bits(bits as u32));
660            }
661        }
662    }
663    // HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to
664    // hide them from the AST.
665    #[cfg(f16_enabled)]
666    macro_rules! f16_tests {
667        () => {
668            #[test]
669            fn test_f16() {
670                let checks = [
671                    ("0x.1234p+16", (0x1234 as f16).to_bits()),
672                    ("0x1.234p+12", (0x1234 as f16).to_bits()),
673                    ("0x12.34p+8", (0x1234 as f16).to_bits()),
674                    ("0x123.4p+4", (0x1234 as f16).to_bits()),
675                    ("0x1234p+0", (0x1234 as f16).to_bits()),
676                    ("0x1234.p+0", (0x1234 as f16).to_bits()),
677                    ("0x1234.0p+0", (0x1234 as f16).to_bits()),
678                    ("0x1.ffcp+15", f16::MAX.to_bits()),
679                    ("0x1.0p+1", 2.0f16.to_bits()),
680                    ("0x1.0p+0", 1.0f16.to_bits()),
681                    ("0x1.ffp+8", 0x5ffc),
682                    ("+0x1.ffp+8", 0x5ffc),
683                    ("0x1p+0", 0x3c00),
684                    ("0x1.998p-4", 0x2e66),
685                    ("0x1.9p+6", 0x5640),
686                    ("0x0.0p0", 0.0f16.to_bits()),
687                    ("-0x0.0p0", (-0.0f16).to_bits()),
688                    ("0x1.0p0", 1.0f16.to_bits()),
689                    ("0x1.998p-4", (0.1f16).to_bits()),
690                    ("-0x1.998p-4", (-0.1f16).to_bits()),
691                    ("0x0.123p-12", 0x0123),
692                    ("0x1p-24", 0x0001),
693                    ("nan", f16::NAN.to_bits()),
694                    ("-nan", (-f16::NAN).to_bits()),
695                    ("inf", f16::INFINITY.to_bits()),
696                    ("-inf", f16::NEG_INFINITY.to_bits()),
697                ];
698                for (s, exp) in checks {
699                    println!("parsing {s}");
700                    assert!(rounding_properties(s).is_ok());
701                    let act = hf16(s).to_bits();
702                    assert_eq!(
703                        act, exp,
704                        "parsing {s}: {act:#06x} != {exp:#06x}\nact: {act:#018b}\nexp: {exp:#018b}"
705                    );
706                }
707            }
708
709            #[test]
710            fn test_macros_f16() {
711                assert_eq!(hf16!("0x1.ffp+8").to_bits(), 0x5ffc_u16);
712            }
713        };
714    }
715
716    #[cfg(f16_enabled)]
717    f16_tests!();
718
719    #[test]
720    fn test_f32() {
721        let checks = [
722            ("0x.1234p+16", (0x1234 as f32).to_bits()),
723            ("0x1.234p+12", (0x1234 as f32).to_bits()),
724            ("0x12.34p+8", (0x1234 as f32).to_bits()),
725            ("0x123.4p+4", (0x1234 as f32).to_bits()),
726            ("0x1234p+0", (0x1234 as f32).to_bits()),
727            ("0x1234.p+0", (0x1234 as f32).to_bits()),
728            ("0x1234.0p+0", (0x1234 as f32).to_bits()),
729            ("0x1.fffffep+127", f32::MAX.to_bits()),
730            ("0x1.0p+1", 2.0f32.to_bits()),
731            ("0x1.0p+0", 1.0f32.to_bits()),
732            ("0x1.ffep+8", 0x43fff000),
733            ("+0x1.ffep+8", 0x43fff000),
734            ("0x1p+0", 0x3f800000),
735            ("0x1.99999ap-4", 0x3dcccccd),
736            ("0x1.9p+6", 0x42c80000),
737            ("0x1.2d5ed2p+20", 0x4996af69),
738            ("-0x1.348eb8p+10", 0xc49a475c),
739            ("-0x1.33dcfep-33", 0xaf19ee7f),
740            ("0x0.0p0", 0.0f32.to_bits()),
741            ("-0x0.0p0", (-0.0f32).to_bits()),
742            ("0x1.0p0", 1.0f32.to_bits()),
743            ("0x1.99999ap-4", (0.1f32).to_bits()),
744            ("-0x1.99999ap-4", (-0.1f32).to_bits()),
745            ("0x1.111114p-127", 0x00444445),
746            ("0x1.23456p-130", 0x00091a2b),
747            ("0x1p-149", 0x00000001),
748            ("nan", f32::NAN.to_bits()),
749            ("-nan", (-f32::NAN).to_bits()),
750            ("inf", f32::INFINITY.to_bits()),
751            ("-inf", f32::NEG_INFINITY.to_bits()),
752        ];
753        for (s, exp) in checks {
754            println!("parsing {s}");
755            let act = hf32(s).to_bits();
756            assert_eq!(
757                act, exp,
758                "parsing {s}: {act:#010x} != {exp:#010x}\nact: {act:#034b}\nexp: {exp:#034b}"
759            );
760        }
761    }
762
763    #[test]
764    fn test_f64() {
765        let checks = [
766            ("0x.1234p+16", (0x1234 as f64).to_bits()),
767            ("0x1.234p+12", (0x1234 as f64).to_bits()),
768            ("0x12.34p+8", (0x1234 as f64).to_bits()),
769            ("0x123.4p+4", (0x1234 as f64).to_bits()),
770            ("0x1234p+0", (0x1234 as f64).to_bits()),
771            ("0x1234.p+0", (0x1234 as f64).to_bits()),
772            ("0x1234.0p+0", (0x1234 as f64).to_bits()),
773            ("0x1.ffep+8", 0x407ffe0000000000),
774            ("0x1p+0", 0x3ff0000000000000),
775            ("0x1.999999999999ap-4", 0x3fb999999999999a),
776            ("0x1.9p+6", 0x4059000000000000),
777            ("0x1.2d5ed1fe1da7bp+20", 0x4132d5ed1fe1da7b),
778            ("-0x1.348eb851eb852p+10", 0xc09348eb851eb852),
779            ("-0x1.33dcfe54a3803p-33", 0xbde33dcfe54a3803),
780            ("0x1.0p0", 1.0f64.to_bits()),
781            ("0x0.0p0", 0.0f64.to_bits()),
782            ("-0x0.0p0", (-0.0f64).to_bits()),
783            ("0x1.999999999999ap-4", 0.1f64.to_bits()),
784            ("0x1.999999999998ap-4", (0.1f64 - f64::EPSILON).to_bits()),
785            ("-0x1.999999999999ap-4", (-0.1f64).to_bits()),
786            ("-0x1.999999999998ap-4", (-0.1f64 + f64::EPSILON).to_bits()),
787            ("0x0.8000000000001p-1022", 0x0008000000000001),
788            ("0x0.123456789abcdp-1022", 0x000123456789abcd),
789            ("0x0.0000000000002p-1022", 0x0000000000000002),
790            ("nan", f64::NAN.to_bits()),
791            ("-nan", (-f64::NAN).to_bits()),
792            ("inf", f64::INFINITY.to_bits()),
793            ("-inf", f64::NEG_INFINITY.to_bits()),
794        ];
795        for (s, exp) in checks {
796            println!("parsing {s}");
797            let act = hf64(s).to_bits();
798            assert_eq!(
799                act, exp,
800                "parsing {s}: {act:#018x} != {exp:#018x}\nact: {act:#066b}\nexp: {exp:#066b}"
801            );
802        }
803    }
804
805    // HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to
806    // hide them from the AST.
807    #[cfg(f128_enabled)]
808    macro_rules! f128_tests {
809        () => {
810            #[test]
811            fn test_f128() {
812                let checks = [
813                    ("0x.1234p+16", (0x1234 as f128).to_bits()),
814                    ("0x1.234p+12", (0x1234 as f128).to_bits()),
815                    ("0x12.34p+8", (0x1234 as f128).to_bits()),
816                    ("0x123.4p+4", (0x1234 as f128).to_bits()),
817                    ("0x1234p+0", (0x1234 as f128).to_bits()),
818                    ("0x1234.p+0", (0x1234 as f128).to_bits()),
819                    ("0x1234.0p+0", (0x1234 as f128).to_bits()),
820                    ("0x1.ffffffffffffffffffffffffffffp+16383", f128::MAX.to_bits()),
821                    ("0x1.0p+1", 2.0f128.to_bits()),
822                    ("0x1.0p+0", 1.0f128.to_bits()),
823                    ("0x1.ffep+8", 0x4007ffe0000000000000000000000000),
824                    ("+0x1.ffep+8", 0x4007ffe0000000000000000000000000),
825                    ("0x1p+0", 0x3fff0000000000000000000000000000),
826                    ("0x1.999999999999999999999999999ap-4", 0x3ffb999999999999999999999999999a),
827                    ("0x1.9p+6", 0x40059000000000000000000000000000),
828                    ("0x0.0p0", 0.0f128.to_bits()),
829                    ("-0x0.0p0", (-0.0f128).to_bits()),
830                    ("0x1.0p0", 1.0f128.to_bits()),
831                    ("0x1.999999999999999999999999999ap-4", (0.1f128).to_bits()),
832                    ("-0x1.999999999999999999999999999ap-4", (-0.1f128).to_bits()),
833                    ("0x0.abcdef0123456789abcdef012345p-16382", 0x0000abcdef0123456789abcdef012345),
834                    ("0x1p-16494", 0x00000000000000000000000000000001),
835                    ("nan", f128::NAN.to_bits()),
836                    ("-nan", (-f128::NAN).to_bits()),
837                    ("inf", f128::INFINITY.to_bits()),
838                    ("-inf", f128::NEG_INFINITY.to_bits()),
839                ];
840                for (s, exp) in checks {
841                    println!("parsing {s}");
842                    let act = hf128(s).to_bits();
843                    assert_eq!(
844                        act, exp,
845                        "parsing {s}: {act:#034x} != {exp:#034x}\nact: {act:#0130b}\nexp: {exp:#0130b}"
846                    );
847                }
848            }
849
850            #[test]
851            fn test_macros_f128() {
852                assert_eq!(hf128!("0x1.ffep+8").to_bits(), 0x4007ffe0000000000000000000000000_u128);
853            }
854        }
855    }
856
857    #[cfg(f128_enabled)]
858    f128_tests!();
859
860    #[test]
861    fn test_macros() {
862        #[cfg(f16_enabled)]
863        assert_eq!(hf16!("0x1.ffp+8").to_bits(), 0x5ffc_u16);
864        assert_eq!(hf32!("0x1.ffep+8").to_bits(), 0x43fff000_u32);
865        assert_eq!(hf64!("0x1.ffep+8").to_bits(), 0x407ffe0000000000_u64);
866        #[cfg(f128_enabled)]
867        assert_eq!(
868            hf128!("0x1.ffep+8").to_bits(),
869            0x4007ffe0000000000000000000000000_u128
870        );
871    }
872}
873
874#[cfg(test)]
875// FIXME(ppc): something with `should_panic` tests cause a SIGILL with ppc64le
876#[cfg(not(all(target_arch = "powerpc64", target_endian = "little")))]
877mod tests_panicking {
878    extern crate std;
879    use super::*;
880
881    // HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to
882    // hide them from the AST.
883    #[cfg(f16_enabled)]
884    macro_rules! f16_tests {
885        () => {
886            #[test]
887            fn test_f16_almost_extra_precision() {
888                // Exact maximum precision allowed
889                hf16("0x1.ffcp+0");
890            }
891
892            #[test]
893            #[should_panic(expected = "the value is too precise")]
894            fn test_f16_extra_precision() {
895                // One bit more than the above.
896                hf16("0x1.ffdp+0");
897            }
898
899            #[test]
900            #[should_panic(expected = "the value is too huge")]
901            fn test_f16_overflow() {
902                // One bit more than the above.
903                hf16("0x1p+16");
904            }
905
906            #[test]
907            fn test_f16_tiniest() {
908                let x = hf16("0x1.p-24");
909                let y = hf16("0x0.001p-12");
910                let z = hf16("0x0.8p-23");
911                assert_eq!(x, y);
912                assert_eq!(x, z);
913            }
914
915            #[test]
916            #[should_panic(expected = "the value is too tiny")]
917            fn test_f16_too_tiny() {
918                hf16("0x1.p-25");
919            }
920
921            #[test]
922            #[should_panic(expected = "the value is too tiny")]
923            fn test_f16_also_too_tiny() {
924                hf16("0x0.8p-24");
925            }
926
927            #[test]
928            #[should_panic(expected = "the value is too tiny")]
929            fn test_f16_again_too_tiny() {
930                hf16("0x0.001p-13");
931            }
932        };
933    }
934
935    #[cfg(f16_enabled)]
936    f16_tests!();
937
938    #[test]
939    fn test_f32_almost_extra_precision() {
940        // Exact maximum precision allowed
941        hf32("0x1.abcdeep+0");
942    }
943
944    #[test]
945    #[should_panic]
946    fn test_f32_extra_precision2() {
947        // One bit more than the above.
948        hf32("0x1.ffffffp+127");
949    }
950
951    #[test]
952    #[should_panic(expected = "the value is too huge")]
953    fn test_f32_overflow() {
954        // One bit more than the above.
955        hf32("0x1p+128");
956    }
957
958    #[test]
959    #[should_panic(expected = "the value is too precise")]
960    fn test_f32_extra_precision() {
961        // One bit more than the above.
962        hf32("0x1.abcdefp+0");
963    }
964
965    #[test]
966    fn test_f32_tiniest() {
967        let x = hf32("0x1.p-149");
968        let y = hf32("0x0.0000000000000001p-85");
969        let z = hf32("0x0.8p-148");
970        assert_eq!(x, y);
971        assert_eq!(x, z);
972    }
973
974    #[test]
975    #[should_panic(expected = "the value is too tiny")]
976    fn test_f32_too_tiny() {
977        hf32("0x1.p-150");
978    }
979
980    #[test]
981    #[should_panic(expected = "the value is too tiny")]
982    fn test_f32_also_too_tiny() {
983        hf32("0x0.8p-149");
984    }
985
986    #[test]
987    #[should_panic(expected = "the value is too tiny")]
988    fn test_f32_again_too_tiny() {
989        hf32("0x0.0000000000000001p-86");
990    }
991
992    #[test]
993    fn test_f64_almost_extra_precision() {
994        // Exact maximum precision allowed
995        hf64("0x1.abcdabcdabcdfp+0");
996    }
997
998    #[test]
999    #[should_panic(expected = "the value is too precise")]
1000    fn test_f64_extra_precision() {
1001        // One bit more than the above.
1002        hf64("0x1.abcdabcdabcdf8p+0");
1003    }
1004
1005    // HACK(msrv): 1.63 rejects unknown width float literals at an AST level, so use a macro to
1006    // hide them from the AST.
1007    #[cfg(f128_enabled)]
1008    macro_rules! f128_tests {
1009        () => {
1010            #[test]
1011            fn test_f128_almost_extra_precision() {
1012                // Exact maximum precision allowed
1013                hf128("0x1.ffffffffffffffffffffffffffffp+16383");
1014            }
1015
1016            #[test]
1017            #[should_panic(expected = "the value is too precise")]
1018            fn test_f128_extra_precision() {
1019                // Just below the maximum finite.
1020                hf128("0x1.fffffffffffffffffffffffffffe8p+16383");
1021            }
1022            #[test]
1023            #[should_panic(expected = "the value is too huge")]
1024            fn test_f128_extra_precision_overflow() {
1025                // One bit more than the above. Should overflow.
1026                hf128("0x1.ffffffffffffffffffffffffffff8p+16383");
1027            }
1028
1029            #[test]
1030            #[should_panic(expected = "the value is too huge")]
1031            fn test_f128_overflow() {
1032                // One bit more than the above.
1033                hf128("0x1p+16384");
1034            }
1035
1036            #[test]
1037            fn test_f128_tiniest() {
1038                let x = hf128("0x1.p-16494");
1039                let y = hf128("0x0.0000000000000001p-16430");
1040                let z = hf128("0x0.8p-16493");
1041                assert_eq!(x, y);
1042                assert_eq!(x, z);
1043            }
1044
1045            #[test]
1046            #[should_panic(expected = "the value is too tiny")]
1047            fn test_f128_too_tiny() {
1048                hf128("0x1.p-16495");
1049            }
1050
1051            #[test]
1052            #[should_panic(expected = "the value is too tiny")]
1053            fn test_f128_again_too_tiny() {
1054                hf128("0x0.0000000000000001p-16431");
1055            }
1056
1057            #[test]
1058            #[should_panic(expected = "the value is too tiny")]
1059            fn test_f128_also_too_tiny() {
1060                hf128("0x0.8p-16494");
1061            }
1062        };
1063    }
1064
1065    #[cfg(f128_enabled)]
1066    f128_tests!();
1067}
1068
1069#[cfg(test)]
1070mod print_tests {
1071    extern crate std;
1072    use std::string::ToString;
1073
1074    use super::*;
1075    use crate::support::Float;
1076
1077    #[test]
1078    #[cfg(f16_enabled)]
1079    fn test_f16() {
1080        use std::format;
1081        // Exhaustively check that `f16` roundtrips.
1082        for x in 0..=u16::MAX {
1083            let f = f16::from_bits(x);
1084            let s = format!("{}", Hexf(f));
1085            let from_s = hf16(&s);
1086
1087            if f.is_nan() && from_s.is_nan() {
1088                continue;
1089            }
1090
1091            assert_eq!(
1092                f.to_bits(),
1093                from_s.to_bits(),
1094                "{f:?} formatted as {s} but parsed as {from_s:?}"
1095            );
1096        }
1097    }
1098
1099    #[test]
1100    #[cfg(f16_enabled)]
1101    fn test_f16_to_f32() {
1102        use std::format;
1103        // Exhaustively check that these are equivalent for all `f16`:
1104        //  - `f16 -> f32`
1105        //  - `f16 -> str -> f32`
1106        //  - `f16 -> f32 -> str -> f32`
1107        //  - `f16 -> f32 -> str -> f16 -> f32`
1108        for x in 0..=u16::MAX {
1109            let f16 = f16::from_bits(x);
1110            let s16 = format!("{}", Hexf(f16));
1111            let f32 = f16 as f32;
1112            let s32 = format!("{}", Hexf(f32));
1113
1114            let a = hf32(&s16);
1115            let b = hf32(&s32);
1116            let c = hf16(&s32);
1117
1118            if f32.is_nan() && a.is_nan() && b.is_nan() && c.is_nan() {
1119                continue;
1120            }
1121
1122            assert_eq!(
1123                f32.to_bits(),
1124                a.to_bits(),
1125                "{f16:?} : f16 formatted as {s16} which parsed as {a:?} : f16"
1126            );
1127            assert_eq!(
1128                f32.to_bits(),
1129                b.to_bits(),
1130                "{f32:?} : f32 formatted as {s32} which parsed as {b:?} : f32"
1131            );
1132            assert_eq!(
1133                f32.to_bits(),
1134                (c as f32).to_bits(),
1135                "{f32:?} : f32 formatted as {s32} which parsed as {c:?} : f16"
1136            );
1137        }
1138    }
1139    #[test]
1140    fn spot_checks() {
1141        assert_eq!(Hexf(f32::MAX).to_string(), "0x1.fffffep+127");
1142        assert_eq!(Hexf(f64::MAX).to_string(), "0x1.fffffffffffffp+1023");
1143
1144        assert_eq!(Hexf(f32::MIN).to_string(), "-0x1.fffffep+127");
1145        assert_eq!(Hexf(f64::MIN).to_string(), "-0x1.fffffffffffffp+1023");
1146
1147        assert_eq!(Hexf(f32::ZERO).to_string(), "0x0p+0");
1148        assert_eq!(Hexf(f64::ZERO).to_string(), "0x0p+0");
1149
1150        assert_eq!(Hexf(f32::NEG_ZERO).to_string(), "-0x0p+0");
1151        assert_eq!(Hexf(f64::NEG_ZERO).to_string(), "-0x0p+0");
1152
1153        assert_eq!(Hexf(f32::NAN).to_string(), "NaN");
1154        assert_eq!(Hexf(f64::NAN).to_string(), "NaN");
1155
1156        assert_eq!(Hexf(f32::INFINITY).to_string(), "inf");
1157        assert_eq!(Hexf(f64::INFINITY).to_string(), "inf");
1158
1159        assert_eq!(Hexf(f32::NEG_INFINITY).to_string(), "-inf");
1160        assert_eq!(Hexf(f64::NEG_INFINITY).to_string(), "-inf");
1161
1162        #[cfg(f16_enabled)]
1163        {
1164            assert_eq!(Hexf(f16::MAX).to_string(), "0x1.ffcp+15");
1165            assert_eq!(Hexf(f16::MIN).to_string(), "-0x1.ffcp+15");
1166            assert_eq!(Hexf(f16::ZERO).to_string(), "0x0p+0");
1167            assert_eq!(Hexf(f16::NEG_ZERO).to_string(), "-0x0p+0");
1168            assert_eq!(Hexf(f16::NAN).to_string(), "NaN");
1169            assert_eq!(Hexf(f16::INFINITY).to_string(), "inf");
1170            assert_eq!(Hexf(f16::NEG_INFINITY).to_string(), "-inf");
1171        }
1172
1173        #[cfg(f128_enabled)]
1174        {
1175            assert_eq!(
1176                Hexf(f128::MAX).to_string(),
1177                "0x1.ffffffffffffffffffffffffffffp+16383"
1178            );
1179            assert_eq!(
1180                Hexf(f128::MIN).to_string(),
1181                "-0x1.ffffffffffffffffffffffffffffp+16383"
1182            );
1183            assert_eq!(Hexf(f128::ZERO).to_string(), "0x0p+0");
1184            assert_eq!(Hexf(f128::NEG_ZERO).to_string(), "-0x0p+0");
1185            assert_eq!(Hexf(f128::NAN).to_string(), "NaN");
1186            assert_eq!(Hexf(f128::INFINITY).to_string(), "inf");
1187            assert_eq!(Hexf(f128::NEG_INFINITY).to_string(), "-inf");
1188        }
1189    }
1190}