1use core::fmt;
4
5use super::{Float, Round, Status, f32_from_bits, f64_from_bits};
6
7#[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#[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
25pub 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#[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
44pub 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
60pub 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 (true, Round::Positive) => Round::Negative,
90 (true, Round::Negative) => Round::Positive,
91 (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 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 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 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 let uexp = (exp - min_lsb) as u128;
158 let uexp = uexp << sig_bits;
159
160 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 if status.inexact() && bits < (1 << sig_bits) {
171 status = status.with(Status::UNDERFLOW);
172 }
173 bits
174 }
175 _ => {
176 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
187const 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
201const fn shr2_round(mut x: u128, round: Round) -> u128 {
203 let t = (x as u32) & 0b111;
204 x >>= 2;
205 match round {
206 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
215struct Parsed {
217 sig: u128,
219 exp: i32,
220}
221
222const 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 exp += 4;
258 inexact |= digit != 0;
259 }
260 if seen_point {
264 exp -= 4;
265 }
266 }
267 }
268 }
269 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 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
347const fn u128_ilog2(v: u128) -> u32 {
351 assert!(v != 0);
352 u128::BITS - 1 - v.leading_zeros()
353}
354
355pub struct Hexf<F>(pub F);
357
358#[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 let mshift = (4 - (F::SIG_BITS % 4)) % 4;
379 let sig = sig << mshift;
380 let mwidth = (F::SIG_BITS as usize + 3) / 4;
382 let leading = if exponent == -bias {
383 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 if let Status::OK = s0 {
500 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 #[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 #[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 #[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#[cfg(not(all(target_arch = "powerpc64", target_endian = "little")))]
869mod tests_panicking {
870 extern crate std;
871 use super::*;
872
873 #[cfg(f16_enabled)]
876 macro_rules! f16_tests {
877 () => {
878 #[test]
879 fn test_f16_almost_extra_precision() {
880 hf16("0x1.ffcp+0");
882 }
883
884 #[test]
885 #[should_panic(expected = "the value is too precise")]
886 fn test_f16_extra_precision() {
887 hf16("0x1.ffdp+0");
889 }
890
891 #[test]
892 #[should_panic(expected = "the value is too huge")]
893 fn test_f16_overflow() {
894 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 hf32("0x1.abcdeep+0");
934 }
935
936 #[test]
937 #[should_panic]
938 fn test_f32_extra_precision2() {
939 hf32("0x1.ffffffp+127");
941 }
942
943 #[test]
944 #[should_panic(expected = "the value is too huge")]
945 fn test_f32_overflow() {
946 hf32("0x1p+128");
948 }
949
950 #[test]
951 #[should_panic(expected = "the value is too precise")]
952 fn test_f32_extra_precision() {
953 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 hf64("0x1.abcdabcdabcdfp+0");
988 }
989
990 #[test]
991 #[should_panic(expected = "the value is too precise")]
992 fn test_f64_extra_precision() {
993 hf64("0x1.abcdabcdabcdf8p+0");
995 }
996
997 #[cfg(f128_enabled)]
1000 macro_rules! f128_tests {
1001 () => {
1002 #[test]
1003 fn test_f128_almost_extra_precision() {
1004 hf128("0x1.ffffffffffffffffffffffffffffp+16383");
1006 }
1007
1008 #[test]
1009 #[should_panic(expected = "the value is too precise")]
1010 fn test_f128_extra_precision() {
1011 hf128("0x1.fffffffffffffffffffffffffffe8p+16383");
1013 }
1014 #[test]
1015 #[should_panic(expected = "the value is too huge")]
1016 fn test_f128_extra_precision_overflow() {
1017 hf128("0x1.ffffffffffffffffffffffffffff8p+16383");
1019 }
1020
1021 #[test]
1022 #[should_panic(expected = "the value is too huge")]
1023 fn test_f128_overflow() {
1024 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 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 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}