@@ -796,6 +796,31 @@ pub trait FloatCore: Num + NumCast + Neg<Output = Self> + PartialOrd + Copy {
796796 /// check(f64::NEG_INFINITY, 1 << 52, 972, -1);
797797 /// ```
798798 fn integer_decode ( self ) -> ( u64 , i16 , i8 ) ;
799+
800+ /// Rounds to the nearest integer, with ties biasing towards an even result.
801+ fn round_ties_even ( self ) -> Self {
802+ let half = ( Self :: one ( ) + Self :: one ( ) ) . recip ( ) ;
803+
804+ if self . fract ( ) . abs ( ) != half {
805+ self . round ( )
806+ } else {
807+ let i = self . abs ( ) . trunc ( ) ;
808+
809+ let value = if ( i * half) . fract ( ) == half {
810+ // -1.5, 1.5, 3.5, ...
811+ self . abs ( ) + half
812+ } else {
813+ // -0.5, 0.5, 2.5, ...
814+ self . abs ( ) - half
815+ } ;
816+
817+ if self . signum ( ) != value. signum ( ) {
818+ -value
819+ } else {
820+ value
821+ }
822+ }
823+ }
799824}
800825
801826impl FloatCore for f32 {
@@ -844,6 +869,11 @@ impl FloatCore for f32 {
844869 Self :: powi( self , n: i32 ) -> Self ;
845870 }
846871
872+ #[ cfg( all( feature = "std" , feature = "msrv_1_77" ) ) ]
873+ forward ! {
874+ Self :: round_ties_even( self ) -> Self ;
875+ }
876+
847877 #[ cfg( all( not( feature = "std" ) , feature = "libm" ) ) ]
848878 forward ! {
849879 libm:: floorf as floor( self ) -> Self ;
@@ -906,6 +936,11 @@ impl FloatCore for f64 {
906936 Self :: powi( self , n: i32 ) -> Self ;
907937 }
908938
939+ #[ cfg( all( feature = "std" , feature = "msrv_1_77" ) ) ]
940+ forward ! {
941+ Self :: round_ties_even( self ) -> Self ;
942+ }
943+
909944 #[ cfg( all( not( feature = "std" ) , feature = "libm" ) ) ]
910945 forward ! {
911946 libm:: floor as floor( self ) -> Self ;
@@ -2510,4 +2545,170 @@ mod tests {
25102545 check_lt ( f32:: INFINITY , f32:: NAN ) ;
25112546 check_gt ( f32:: NAN , 1.0_f32 ) ;
25122547 }
2548+
2549+ /// Compares the fallback implementation of [`round_ties_even`] to the one provided by `f32`.`
2550+ ///
2551+ /// [`round_ties_even`]: crate::float::FloatCore::round_ties_even
2552+ #[ cfg( feature = "msrv_1_77" ) ]
2553+ #[ test]
2554+ fn round_ties_even ( ) {
2555+ mod wrapped_f32 {
2556+ use crate :: { float:: FloatCore , Num , NumCast , One , ToPrimitive , Zero } ;
2557+ use core:: ops:: { Add , Div , Mul , Neg , Rem , Sub } ;
2558+
2559+ #[ derive( Clone , Copy , PartialEq , PartialOrd , Debug ) ]
2560+ pub struct WrappedF32 ( pub f32 ) ;
2561+
2562+ impl ToPrimitive for WrappedF32 {
2563+ fn to_i64 ( & self ) -> Option < i64 > {
2564+ f32:: to_i64 ( & self . 0 )
2565+ }
2566+
2567+ fn to_u64 ( & self ) -> Option < u64 > {
2568+ f32:: to_u64 ( & self . 0 )
2569+ }
2570+ }
2571+
2572+ impl NumCast for WrappedF32 {
2573+ fn from < T : crate :: ToPrimitive > ( n : T ) -> Option < Self > {
2574+ Some ( Self ( <f32 as NumCast >:: from ( n) ?) )
2575+ }
2576+ }
2577+
2578+ impl Neg for WrappedF32 {
2579+ type Output = Self ;
2580+
2581+ fn neg ( self ) -> Self :: Output {
2582+ Self ( self . 0 . neg ( ) )
2583+ }
2584+ }
2585+
2586+ impl Mul for WrappedF32 {
2587+ type Output = Self ;
2588+
2589+ fn mul ( self , rhs : Self ) -> Self :: Output {
2590+ Self ( f32:: mul ( self . 0 , rhs. 0 ) )
2591+ }
2592+ }
2593+
2594+ impl Add for WrappedF32 {
2595+ type Output = Self ;
2596+
2597+ fn add ( self , rhs : Self ) -> Self :: Output {
2598+ Self ( f32:: add ( self . 0 , rhs. 0 ) )
2599+ }
2600+ }
2601+
2602+ impl Rem for WrappedF32 {
2603+ type Output = Self ;
2604+
2605+ fn rem ( self , rhs : Self ) -> Self :: Output {
2606+ Self ( f32:: rem ( self . 0 , rhs. 0 ) )
2607+ }
2608+ }
2609+
2610+ impl Div for WrappedF32 {
2611+ type Output = Self ;
2612+
2613+ fn div ( self , rhs : Self ) -> Self :: Output {
2614+ Self ( f32:: div ( self . 0 , rhs. 0 ) )
2615+ }
2616+ }
2617+
2618+ impl Sub for WrappedF32 {
2619+ type Output = Self ;
2620+
2621+ fn sub ( self , rhs : Self ) -> Self :: Output {
2622+ Self ( f32:: sub ( self . 0 , rhs. 0 ) )
2623+ }
2624+ }
2625+
2626+ impl One for WrappedF32 {
2627+ fn one ( ) -> Self {
2628+ Self ( f32:: one ( ) )
2629+ }
2630+ }
2631+
2632+ impl Zero for WrappedF32 {
2633+ fn zero ( ) -> Self {
2634+ Self ( f32:: zero ( ) )
2635+ }
2636+
2637+ fn is_zero ( & self ) -> bool {
2638+ self . 0 . is_zero ( )
2639+ }
2640+ }
2641+
2642+ impl Num for WrappedF32 {
2643+ type FromStrRadixErr = <f32 as Num >:: FromStrRadixErr ;
2644+
2645+ fn from_str_radix ( str : & str , radix : u32 ) -> Result < Self , Self :: FromStrRadixErr > {
2646+ Ok ( Self ( f32:: from_str_radix ( str, radix) ?) )
2647+ }
2648+ }
2649+
2650+ impl FloatCore for WrappedF32 {
2651+ fn infinity ( ) -> Self {
2652+ Self ( f32:: infinity ( ) )
2653+ }
2654+
2655+ fn neg_infinity ( ) -> Self {
2656+ Self ( f32:: neg_infinity ( ) )
2657+ }
2658+
2659+ fn nan ( ) -> Self {
2660+ Self ( f32:: nan ( ) )
2661+ }
2662+
2663+ fn neg_zero ( ) -> Self {
2664+ Self ( f32:: neg_zero ( ) )
2665+ }
2666+
2667+ fn min_value ( ) -> Self {
2668+ Self ( f32:: min_value ( ) )
2669+ }
2670+
2671+ fn min_positive_value ( ) -> Self {
2672+ Self ( f32:: min_positive_value ( ) )
2673+ }
2674+
2675+ fn epsilon ( ) -> Self {
2676+ Self ( f32:: epsilon ( ) )
2677+ }
2678+
2679+ fn max_value ( ) -> Self {
2680+ Self ( f32:: max_value ( ) )
2681+ }
2682+
2683+ fn classify ( self ) -> core:: num:: FpCategory {
2684+ f32:: classify ( self . 0 )
2685+ }
2686+
2687+ fn to_degrees ( self ) -> Self {
2688+ Self ( f32:: to_degrees ( self . 0 ) )
2689+ }
2690+
2691+ fn to_radians ( self ) -> Self {
2692+ Self ( f32:: to_radians ( self . 0 ) )
2693+ }
2694+
2695+ fn integer_decode ( self ) -> ( u64 , i16 , i8 ) {
2696+ f32:: integer_decode ( self . 0 )
2697+ }
2698+ }
2699+ }
2700+
2701+ use crate :: float:: FloatCore ;
2702+ use wrapped_f32:: WrappedF32 ;
2703+
2704+ for x in [
2705+ -5.0 , -4.5 , -4.0 , -3.5 , -3.0 , -2.5 , -2.0 , -1.5 , -1.0 , -0.5 , 0.0 , 0.5 , 1.0 , 1.5 , 2.0 ,
2706+ 2.5 , 3.0 , 3.5 , 4.0 , 4.5 , 5.0 ,
2707+ ] {
2708+ for dx in -250_000 ..=250_000 {
2709+ let y = x + ( dx as f32 / 1_000_000.0 ) ;
2710+ assert_eq ! ( WrappedF32 ( y) . round_ties_even( ) . 0 , y. round_ties_even( ) ) ;
2711+ }
2712+ }
2713+ }
25132714}
0 commit comments