@@ -85,6 +85,20 @@ macro_rules! llvm_intrinsically_optimized {
85
85
} ;
86
86
}
87
87
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
+
88
102
// Public modules
89
103
mod acos;
90
104
mod acosf;
@@ -369,3 +383,235 @@ fn with_set_low_word(f: f64, lo: u32) -> f64 {
369
383
fn combine_words ( hi : u32 , lo : u32 ) -> f64 {
370
384
f64:: from_bits ( ( hi as u64 ) << 32 | lo as u64 )
371
385
}
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