@@ -85,6 +85,20 @@ macro_rules! llvm_intrinsically_optimized {
8585 } ;
8686}
8787
88+ #[ allow( unused) ]
89+ macro_rules! hf32 {
90+ ( $s: literal) => {
91+ const { $crate:: math:: hex_float:: hf32( $s) }
92+ } ;
93+ }
94+
95+ #[ allow( unused) ]
96+ macro_rules! hf64 {
97+ ( $s: literal) => {
98+ const { $crate:: math:: hex_float:: hf64( $s) }
99+ } ;
100+ }
101+
88102// Public modules
89103mod acos;
90104mod acosf;
@@ -369,3 +383,235 @@ fn with_set_low_word(f: f64, lo: u32) -> f64 {
369383fn combine_words ( hi : u32 , lo : u32 ) -> f64 {
370384 f64:: from_bits ( ( hi as u64 ) << 32 | lo as u64 )
371385}
386+
387+ pub mod hex_float {
388+ pub const fn hf32 ( s : & str ) -> f32 {
389+ f32:: from_bits ( parse_any ( s, 32 , 23 ) as u32 )
390+ }
391+
392+ pub const fn hf64 ( s : & str ) -> f64 {
393+ f64:: from_bits ( parse_any ( s, 64 , 52 ) as u64 )
394+ }
395+
396+ const fn parse_any ( s : & str , bits : u32 , sig_bits : u32 ) -> u128 {
397+ let exp_bits: u32 = bits - sig_bits - 1 ;
398+ let exp_max: i32 = ( 1 << exp_bits) - 2 ;
399+ let exp_bias: i32 = ( 1 << ( exp_bits - 1 ) ) - 1 ;
400+
401+ let b = s. as_bytes ( ) ;
402+
403+ let mut exp: i32 = parse_exp ( b) + exp_bias;
404+ assert ! ( exp <= exp_max) ;
405+
406+ // Out of range exponents are subnormal, this changes
407+ // how we shift bits in.
408+ let subnorm_shift = if exp <= 0 {
409+ let tmp = -exp + 1 ;
410+ exp = 0 ;
411+ tmp
412+ } else {
413+ 0
414+ } ;
415+
416+ let mut i;
417+ let mut next_i = 0 ;
418+ let mut sig: u128 = 0 ;
419+ let mut neg: u128 = 0 ;
420+
421+ // Position relative to digits
422+ let mut digit_idx = 0 ;
423+ let mut start_0x = 0 ;
424+ let mut start_digits;
425+ let mut maybe_zero = true ;
426+
427+ while next_i < b. len ( ) {
428+ next_i += 1 ;
429+ i = next_i - 1 ;
430+ let c = b[ i] ;
431+
432+ if i == 0 && c == b'-' {
433+ neg = 1 ;
434+ start_0x += 1 ;
435+ continue ;
436+ } else if i == 0 && c == b'+' {
437+ start_0x += 1 ;
438+ continue ;
439+ }
440+
441+ start_digits = start_0x + 2 ;
442+
443+ if ( i - start_0x) == 0 {
444+ assert ! ( c == b'0' ) ;
445+ continue ;
446+ } else if ( i - start_0x) == 1 {
447+ assert ! ( c == b'x' ) ;
448+ continue ;
449+ }
450+
451+ if c == b'p' {
452+ break ;
453+ } else if ( i - start_digits) == 0 {
454+ if c == b'0' {
455+ exp -= 1 ;
456+ } else {
457+ assert ! ( c == b'1' ) ;
458+ maybe_zero = false ;
459+ }
460+ } else if ( i - start_digits) == 1 {
461+ assert ! ( c == b'.' ) ;
462+ continue ;
463+ } else {
464+ digit_idx += 1 ;
465+ }
466+
467+ let nibbles = digit_idx * 4 ;
468+ let shift = sig_bits as i32 - subnorm_shift - nibbles;
469+ let d = hex_digit ( c) ;
470+ if d != 0 {
471+ maybe_zero = false ;
472+ }
473+
474+ if shift > 0 {
475+ // Shift the hex digits into the mantissa
476+ // Note: the implicit bit is included and needs to be cleared
477+ // later (leading `1.` gets parsed as the implicit bit)
478+ sig |= ( d as u128 ) << shift;
479+ } else if shift > -4 {
480+ // Shift right up to 4 bits
481+ sig |= ( d as u128 ) >> -shift;
482+ }
483+ }
484+
485+ if maybe_zero {
486+ sig = 0 ;
487+ exp = 0 ;
488+ }
489+
490+ // Clear the implicit bit
491+ sig = ( sig << ( 128 - sig_bits) ) >> ( 128 - sig_bits) ;
492+
493+ // Construct a float
494+ ( neg << ( bits - 1 ) ) | ( ( exp as u128 ) << sig_bits) | sig
495+ }
496+
497+ const fn parse_exp ( b : & [ u8 ] ) -> i32 {
498+ let mut i = 0 ;
499+ while i < b. len ( ) {
500+ if b[ i] == b'p' {
501+ i += 1 ;
502+ break ;
503+ }
504+ i += 1 ;
505+ }
506+
507+ let mut ret: i32 = 0 ;
508+ let mut neg = false ;
509+ let mut next_i = i;
510+ let start = i;
511+
512+ while next_i < b. len ( ) {
513+ next_i += 1 ;
514+ i = next_i - 1 ;
515+ let c = b[ i] ;
516+
517+ if i == start && c == b'-' {
518+ neg = true ;
519+ continue ;
520+ } else if i == start && c == b'+' {
521+ continue ;
522+ }
523+
524+ let d = dec_digit ( c) ;
525+ ret *= 10 ;
526+ ret += d as i32 ;
527+ }
528+
529+ if neg {
530+ ret *= -1 ;
531+ }
532+
533+ ret
534+ }
535+
536+ const fn hex_digit ( c : u8 ) -> u8 {
537+ match ( c as char ) . to_digit ( 16 ) {
538+ Some ( v) => v as u8 ,
539+ None => panic ! ( "bad char" ) ,
540+ }
541+ }
542+
543+ const fn dec_digit ( c : u8 ) -> u8 {
544+ match ( c as char ) . to_digit ( 10 ) {
545+ Some ( v) => v as u8 ,
546+ None => panic ! ( "bad char" ) ,
547+ }
548+ }
549+
550+ #[ cfg( test) ]
551+ mod tests {
552+ extern crate std;
553+ use super :: * ;
554+ use std:: println;
555+
556+ #[ test]
557+ fn test_f32 ( ) {
558+ let checks = [
559+ ( "0x1.ffep+8" , 0x43fff000 ) ,
560+ ( "+0x1.ffep+8" , 0x43fff000 ) ,
561+ ( "0x1p+0" , 0x3f800000 ) ,
562+ ( "0x1.99999ap-4" , 0x3dcccccd ) ,
563+ ( "0x1.9p+6" , 0x42c80000 ) ,
564+ ( "0x1.2d5ed2p+20" , 0x4996af69 ) ,
565+ ( "-0x1.348eb8p+10" , 0xc49a475c ) ,
566+ ( "-0x1.33dcfep-33" , 0xaf19ee7f ) ,
567+ ( "0x0.0p0" , 0.0f32 . to_bits ( ) ) ,
568+ ( "-0x0.0p0" , ( -0.0f32 ) . to_bits ( ) ) ,
569+ ( "0x1.0p0" , 1.0f32 . to_bits ( ) ) ,
570+ ( "0x1.99999ap-4" , ( 0.1f32 ) . to_bits ( ) ) ,
571+ ( "-0x1.99999ap-4" , ( -0.1f32 ) . to_bits ( ) ) ,
572+ ( "0x1.111114p-127" , 0x00444445 ) ,
573+ ( "0x1.23456p-130" , 0x00091a2b ) ,
574+ ( "0x1p-149" , 0x00000001 ) ,
575+ ] ;
576+ for ( s, exp) in checks {
577+ println ! ( "parsing {s}" ) ;
578+ let act = hf32 ( s) . to_bits ( ) ;
579+ assert_eq ! (
580+ act, exp,
581+ "parsing {s}: {act:#010x} != {exp:#010x}\n act: {act:#034b}\n exp: {exp:#034b}"
582+ ) ;
583+ }
584+ }
585+
586+ #[ test]
587+ fn test_f64 ( ) {
588+ let checks = [
589+ ( "0x1.ffep+8" , 0x407ffe0000000000 ) ,
590+ ( "0x1p+0" , 0x3ff0000000000000 ) ,
591+ ( "0x1.999999999999ap-4" , 0x3fb999999999999a ) ,
592+ ( "0x1.9p+6" , 0x4059000000000000 ) ,
593+ ( "0x1.2d5ed1fe1da7bp+20" , 0x4132d5ed1fe1da7b ) ,
594+ ( "-0x1.348eb851eb852p+10" , 0xc09348eb851eb852 ) ,
595+ ( "-0x1.33dcfe54a3803p-33" , 0xbde33dcfe54a3803 ) ,
596+ ( "0x1.0p0" , 1.0f64 . to_bits ( ) ) ,
597+ ( "0x0.0p0" , 0.0f64 . to_bits ( ) ) ,
598+ ( "-0x0.0p0" , ( -0.0f64 ) . to_bits ( ) ) ,
599+ ( "0x1.999999999999ap-4" , 0.1f64 . to_bits ( ) ) ,
600+ ( "0x1.999999999998ap-4" , ( 0.1f64 - f64:: EPSILON ) . to_bits ( ) ) ,
601+ ( "-0x1.999999999999ap-4" , ( -0.1f64 ) . to_bits ( ) ) ,
602+ ( "-0x1.999999999998ap-4" , ( -0.1f64 + f64:: EPSILON ) . to_bits ( ) ) ,
603+ ( "0x0.8000000000001p-1022" , 0x0008000000000001 ) ,
604+ ( "0x0.123456789abcdp-1022" , 0x000123456789abcd ) ,
605+ ( "0x0.0000000000002p-1022" , 0x0000000000000002 ) ,
606+ ] ;
607+ for ( s, exp) in checks {
608+ println ! ( "parsing {s}" ) ;
609+ let act = hf64 ( s) . to_bits ( ) ;
610+ assert_eq ! (
611+ act, exp,
612+ "parsing {s}: {act:#018x} != {exp:#018x}\n act: {act:#066b}\n exp: {exp:#066b}"
613+ ) ;
614+ }
615+ }
616+ }
617+ }
0 commit comments