@@ -441,6 +441,129 @@ macro_rules! minifloat {
441441 #[ allow( clippy:: cast_possible_truncation, clippy:: cast_sign_loss) ]
442442 Self :: from_bits( magnitude. min( i64 :: from( Self :: HUGE . to_bits( ) ) ) as $bits | sign_bit)
443443 }
444+
445+ /// Fast conversion to [`f32`]
446+ ///
447+ /// This method serves as a shortcut if conversion to [`f32`] is
448+ /// lossless.
449+ fn fast_to_f32( self ) -> f32 {
450+ let sign = if self . is_sign_negative( ) { -1.0 } else { 1.0 } ;
451+ let magnitude = self . to_bits( ) & Self :: ABS_MASK ;
452+
453+ if self . is_nan( ) {
454+ return f32 :: NAN . copysign( sign) ;
455+ }
456+ if self . is_infinite( ) {
457+ return f32 :: INFINITY * sign;
458+ }
459+ if magnitude < 1 << Self :: M {
460+ #[ allow( clippy:: cast_possible_wrap) ]
461+ let shift = Self :: MIN_EXP - Self :: MANTISSA_DIGITS as i32 ;
462+ #[ allow( clippy:: cast_possible_truncation) ]
463+ return ( $crate:: detail:: exp2i( shift) * f64 :: from( sign) * f64 :: from( magnitude) ) as f32 ;
464+ }
465+ let shift = f32 :: MANTISSA_DIGITS - Self :: MANTISSA_DIGITS ;
466+ #[ allow( clippy:: cast_sign_loss) ]
467+ let diff = ( Self :: MIN_EXP - f32 :: MIN_EXP ) as u32 ;
468+ let diff = diff << ( f32 :: MANTISSA_DIGITS - 1 ) ;
469+ let sign = u32 :: from( self . is_sign_negative( ) ) << 31 ;
470+ f32 :: from_bits( ( ( u32 :: from( magnitude) << shift) + diff) | sign)
471+ }
472+
473+ /// Fast conversion to [`f64`]
474+ ///
475+ /// This method serves as a shortcut if conversion to [`f64`] is
476+ /// lossless.
477+ fn fast_to_f64( self ) -> f64 {
478+ let sign = if self . is_sign_negative( ) { -1.0 } else { 1.0 } ;
479+ let magnitude = self . to_bits( ) & Self :: ABS_MASK ;
480+
481+ if self . is_nan( ) {
482+ return f64 :: NAN . copysign( sign) ;
483+ }
484+ if self . is_infinite( ) {
485+ return f64 :: INFINITY * sign;
486+ }
487+ if magnitude < 1 << Self :: M {
488+ #[ allow( clippy:: cast_possible_wrap) ]
489+ let shift = Self :: MIN_EXP - Self :: MANTISSA_DIGITS as i32 ;
490+ return $crate:: detail:: exp2i( shift) * sign * f64 :: from( magnitude) ;
491+ }
492+ let shift = f64 :: MANTISSA_DIGITS - Self :: MANTISSA_DIGITS ;
493+ #[ allow( clippy:: cast_sign_loss) ]
494+ let diff = ( Self :: MIN_EXP - f64 :: MIN_EXP ) as u64 ;
495+ let diff = diff << ( f64 :: MANTISSA_DIGITS - 1 ) ;
496+ let sign = u64 :: from( self . is_sign_negative( ) ) << 63 ;
497+ f64 :: from_bits( ( ( u64 :: from( magnitude) << shift) + diff) | sign)
498+ }
499+
500+ /// Lossy conversion to [`f64`]
501+ ///
502+ /// This variant assumes that the conversion is lossy only when the exponent
503+ /// is out of range.
504+ fn as_f64( self ) -> f64 {
505+ let bias = ( 1 << ( Self :: E - 1 ) ) - 1 ;
506+ let sign = if self . is_sign_negative( ) { -1.0 } else { 1.0 } ;
507+ let magnitude = self . abs( ) . to_bits( ) ;
508+
509+ if self . is_nan( ) {
510+ return f64 :: NAN . copysign( sign) ;
511+ }
512+ if self . is_infinite( ) {
513+ return f64 :: INFINITY * sign;
514+ }
515+ if i32 :: from( magnitude) >= ( f64 :: MAX_EXP + bias) << Self :: M {
516+ return f64 :: INFINITY * sign;
517+ }
518+ if magnitude < 1 << Self :: M {
519+ #[ allow( clippy:: cast_possible_wrap) ]
520+ let shift = Self :: MIN_EXP - Self :: MANTISSA_DIGITS as i32 ;
521+ return $crate:: detail:: exp2i( shift) * sign * f64 :: from( magnitude) ;
522+ }
523+ if i32 :: from( magnitude >> Self :: M ) < f64 :: MIN_EXP + bias {
524+ let significand = ( magnitude & ( ( 1 << Self :: M ) - 1 ) ) | 1 << Self :: M ;
525+ let exponent = i32 :: from( magnitude >> Self :: M ) - bias;
526+ #[ allow( clippy:: cast_possible_wrap) ]
527+ return $crate:: detail:: exp2i( exponent - Self :: M as i32 ) * sign * f64 :: from( significand) ;
528+ }
529+ let shift = f64 :: MANTISSA_DIGITS - Self :: MANTISSA_DIGITS ;
530+ #[ allow( clippy:: cast_sign_loss) ]
531+ let diff = ( Self :: MIN_EXP - f64 :: MIN_EXP ) as u64 ;
532+ let diff = diff << ( f64 :: MANTISSA_DIGITS - 1 ) ;
533+ let sign = u64 :: from( self . is_sign_negative( ) ) << 63 ;
534+ f64 :: from_bits( ( ( u64 :: from( magnitude) << shift) + diff) | sign)
535+ }
536+
537+ /// Best effort conversion to [`f64`]
538+ #[ must_use]
539+ pub fn to_f64( self ) -> f64 {
540+ let lossless = f64 :: MANTISSA_DIGITS >= Self :: MANTISSA_DIGITS
541+ && f64 :: MAX_EXP >= Self :: MAX_EXP
542+ && f64 :: MIN_EXP <= Self :: MIN_EXP ;
543+
544+ if lossless {
545+ self . fast_to_f64( )
546+ } else {
547+ self . as_f64( )
548+ }
549+ }
550+
551+ /// Best effort conversion to [`f32`]
552+ #[ must_use]
553+ pub fn to_f32( self ) -> f32 {
554+ let lossless = f32 :: MANTISSA_DIGITS >= Self :: MANTISSA_DIGITS
555+ && f32 :: MAX_EXP >= Self :: MAX_EXP
556+ && f32 :: MIN_EXP <= Self :: MIN_EXP ;
557+
558+ if lossless {
559+ return self . fast_to_f32( ) ;
560+ }
561+ // Conversion to `f64` is lossy only when then exponent width is
562+ // too large. In this case, a second conversion to `f32` is
563+ // safe.
564+ #[ allow( clippy:: cast_possible_truncation) ]
565+ return self . to_f64( ) as f32 ;
566+ }
444567 }
445568
446569 const _: ( ) = assert!( $name:: BITWIDTH <= 16 ) ;
0 commit comments