libm/math/support/
hex_float.rs

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