@@ -796,6 +796,49 @@ 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+ ///
802+ /// # Examples
803+ ///
804+ /// ```
805+ /// use num_traits::float::FloatCore;
806+ ///
807+ /// fn check<T: FloatCore>(x: T, rounded: T) {
808+ /// assert!(x.round_ties_even() == rounded);
809+ /// }
810+ ///
811+ /// check(1.0f32, 1.0);
812+ /// check(1.25f32, 1.0);
813+ /// check(1.75f32, 2.0);
814+ /// check(1.5f32, 2.0);
815+ /// check(2.5f32, 2.0);
816+ /// check(3.5f32, 4.0);
817+ /// check(-3.5f32, -4.0);
818+ /// ```
819+ fn round_ties_even ( self ) -> Self {
820+ let half = ( Self :: one ( ) + Self :: one ( ) ) . recip ( ) ;
821+
822+ if self . fract ( ) . abs ( ) != half {
823+ self . round ( )
824+ } else {
825+ let i = self . abs ( ) . trunc ( ) ;
826+
827+ let value = if ( i * half) . fract ( ) == half {
828+ // -1.5, 1.5, 3.5, ...
829+ self . abs ( ) + half
830+ } else {
831+ // -0.5, 0.5, 2.5, ...
832+ self . abs ( ) - half
833+ } ;
834+
835+ if self . signum ( ) != value. signum ( ) {
836+ -value
837+ } else {
838+ value
839+ }
840+ }
841+ }
799842}
800843
801844impl FloatCore for f32 {
@@ -844,6 +887,11 @@ impl FloatCore for f32 {
844887 Self :: powi( self , n: i32 ) -> Self ;
845888 }
846889
890+ #[ cfg( all( feature = "std" , has_round_ties_even) ) ]
891+ forward ! {
892+ Self :: round_ties_even( self ) -> Self ;
893+ }
894+
847895 #[ cfg( all( not( feature = "std" ) , feature = "libm" ) ) ]
848896 forward ! {
849897 libm:: floorf as floor( self ) -> Self ;
@@ -906,6 +954,11 @@ impl FloatCore for f64 {
906954 Self :: powi( self , n: i32 ) -> Self ;
907955 }
908956
957+ #[ cfg( all( feature = "std" , has_round_ties_even) ) ]
958+ forward ! {
959+ Self :: round_ties_even( self ) -> Self ;
960+ }
961+
909962 #[ cfg( all( not( feature = "std" ) , feature = "libm" ) ) ]
910963 forward ! {
911964 libm:: floor as floor( self ) -> Self ;
@@ -1909,6 +1962,49 @@ pub trait Float: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
19091962 self . neg ( )
19101963 }
19111964 }
1965+
1966+ /// Rounds to the nearest integer, with ties biasing towards an even result.
1967+ ///
1968+ /// # Examples
1969+ ///
1970+ /// ```
1971+ /// use num_traits::Float;
1972+ ///
1973+ /// fn check<T: Float>(x: T, rounded: T) {
1974+ /// assert!(x.round_ties_even() == rounded);
1975+ /// }
1976+ ///
1977+ /// check(1.0f32, 1.0);
1978+ /// check(1.25f32, 1.0);
1979+ /// check(1.75f32, 2.0);
1980+ /// check(1.5f32, 2.0);
1981+ /// check(2.5f32, 2.0);
1982+ /// check(3.5f32, 4.0);
1983+ /// check(-3.5f32, -4.0);
1984+ /// ```
1985+ fn round_ties_even ( self ) -> Self {
1986+ let half = ( Self :: one ( ) + Self :: one ( ) ) . recip ( ) ;
1987+
1988+ if self . fract ( ) . abs ( ) != half {
1989+ self . round ( )
1990+ } else {
1991+ let i = self . abs ( ) . trunc ( ) ;
1992+
1993+ let value = if ( i * half) . fract ( ) == half {
1994+ // -1.5, 1.5, 3.5, ...
1995+ self . abs ( ) + half
1996+ } else {
1997+ // -0.5, 0.5, 2.5, ...
1998+ self . abs ( ) - half
1999+ } ;
2000+
2001+ if self . signum ( ) != value. signum ( ) {
2002+ -value
2003+ } else {
2004+ value
2005+ }
2006+ }
2007+ }
19122008}
19132009
19142010#[ cfg( feature = "std" ) ]
@@ -1989,6 +2085,11 @@ macro_rules! float_impl_std {
19892085 Self :: atanh( self ) -> Self ;
19902086 Self :: copysign( self , sign: Self ) -> Self ;
19912087 }
2088+
2089+ #[ cfg( has_round_ties_even) ]
2090+ forward! {
2091+ Self :: round_ties_even( self ) -> Self ;
2092+ }
19922093 }
19932094 } ;
19942095}
@@ -2510,4 +2611,170 @@ mod tests {
25102611 check_lt ( f32:: INFINITY , f32:: NAN ) ;
25112612 check_gt ( f32:: NAN , 1.0_f32 ) ;
25122613 }
2614+
2615+ /// Compares the fallback implementation of [`round_ties_even`] to the one provided by `f32`.`
2616+ ///
2617+ /// [`round_ties_even`]: crate::float::FloatCore::round_ties_even
2618+ #[ cfg( has_round_ties_even) ]
2619+ #[ test]
2620+ fn round_ties_even ( ) {
2621+ mod wrapped_f32 {
2622+ use crate :: { float:: FloatCore , Num , NumCast , One , ToPrimitive , Zero } ;
2623+ use core:: ops:: { Add , Div , Mul , Neg , Rem , Sub } ;
2624+
2625+ #[ derive( Clone , Copy , PartialEq , PartialOrd , Debug ) ]
2626+ pub struct WrappedF32 ( pub f32 ) ;
2627+
2628+ impl ToPrimitive for WrappedF32 {
2629+ fn to_i64 ( & self ) -> Option < i64 > {
2630+ f32:: to_i64 ( & self . 0 )
2631+ }
2632+
2633+ fn to_u64 ( & self ) -> Option < u64 > {
2634+ f32:: to_u64 ( & self . 0 )
2635+ }
2636+ }
2637+
2638+ impl NumCast for WrappedF32 {
2639+ fn from < T : crate :: ToPrimitive > ( n : T ) -> Option < Self > {
2640+ Some ( Self ( <f32 as NumCast >:: from ( n) ?) )
2641+ }
2642+ }
2643+
2644+ impl Neg for WrappedF32 {
2645+ type Output = Self ;
2646+
2647+ fn neg ( self ) -> Self :: Output {
2648+ Self ( self . 0 . neg ( ) )
2649+ }
2650+ }
2651+
2652+ impl Mul for WrappedF32 {
2653+ type Output = Self ;
2654+
2655+ fn mul ( self , rhs : Self ) -> Self :: Output {
2656+ Self ( f32:: mul ( self . 0 , rhs. 0 ) )
2657+ }
2658+ }
2659+
2660+ impl Add for WrappedF32 {
2661+ type Output = Self ;
2662+
2663+ fn add ( self , rhs : Self ) -> Self :: Output {
2664+ Self ( f32:: add ( self . 0 , rhs. 0 ) )
2665+ }
2666+ }
2667+
2668+ impl Rem for WrappedF32 {
2669+ type Output = Self ;
2670+
2671+ fn rem ( self , rhs : Self ) -> Self :: Output {
2672+ Self ( f32:: rem ( self . 0 , rhs. 0 ) )
2673+ }
2674+ }
2675+
2676+ impl Div for WrappedF32 {
2677+ type Output = Self ;
2678+
2679+ fn div ( self , rhs : Self ) -> Self :: Output {
2680+ Self ( f32:: div ( self . 0 , rhs. 0 ) )
2681+ }
2682+ }
2683+
2684+ impl Sub for WrappedF32 {
2685+ type Output = Self ;
2686+
2687+ fn sub ( self , rhs : Self ) -> Self :: Output {
2688+ Self ( f32:: sub ( self . 0 , rhs. 0 ) )
2689+ }
2690+ }
2691+
2692+ impl One for WrappedF32 {
2693+ fn one ( ) -> Self {
2694+ Self ( f32:: one ( ) )
2695+ }
2696+ }
2697+
2698+ impl Zero for WrappedF32 {
2699+ fn zero ( ) -> Self {
2700+ Self ( f32:: zero ( ) )
2701+ }
2702+
2703+ fn is_zero ( & self ) -> bool {
2704+ self . 0 . is_zero ( )
2705+ }
2706+ }
2707+
2708+ impl Num for WrappedF32 {
2709+ type FromStrRadixErr = <f32 as Num >:: FromStrRadixErr ;
2710+
2711+ fn from_str_radix ( str : & str , radix : u32 ) -> Result < Self , Self :: FromStrRadixErr > {
2712+ Ok ( Self ( f32:: from_str_radix ( str, radix) ?) )
2713+ }
2714+ }
2715+
2716+ impl FloatCore for WrappedF32 {
2717+ fn infinity ( ) -> Self {
2718+ Self ( f32:: infinity ( ) )
2719+ }
2720+
2721+ fn neg_infinity ( ) -> Self {
2722+ Self ( f32:: neg_infinity ( ) )
2723+ }
2724+
2725+ fn nan ( ) -> Self {
2726+ Self ( f32:: nan ( ) )
2727+ }
2728+
2729+ fn neg_zero ( ) -> Self {
2730+ Self ( f32:: neg_zero ( ) )
2731+ }
2732+
2733+ fn min_value ( ) -> Self {
2734+ Self ( f32:: min_value ( ) )
2735+ }
2736+
2737+ fn min_positive_value ( ) -> Self {
2738+ Self ( f32:: min_positive_value ( ) )
2739+ }
2740+
2741+ fn epsilon ( ) -> Self {
2742+ Self ( f32:: epsilon ( ) )
2743+ }
2744+
2745+ fn max_value ( ) -> Self {
2746+ Self ( f32:: max_value ( ) )
2747+ }
2748+
2749+ fn classify ( self ) -> core:: num:: FpCategory {
2750+ f32:: classify ( self . 0 )
2751+ }
2752+
2753+ fn to_degrees ( self ) -> Self {
2754+ Self ( f32:: to_degrees ( self . 0 ) )
2755+ }
2756+
2757+ fn to_radians ( self ) -> Self {
2758+ Self ( f32:: to_radians ( self . 0 ) )
2759+ }
2760+
2761+ fn integer_decode ( self ) -> ( u64 , i16 , i8 ) {
2762+ f32:: integer_decode ( self . 0 )
2763+ }
2764+ }
2765+ }
2766+
2767+ use crate :: float:: FloatCore ;
2768+ use wrapped_f32:: WrappedF32 ;
2769+
2770+ for x in [
2771+ -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 ,
2772+ 2.5 , 3.0 , 3.5 , 4.0 , 4.5 , 5.0 ,
2773+ ] {
2774+ for dx in -250_000 ..=250_000 {
2775+ let y = x + ( dx as f32 / 1_000_000.0 ) ;
2776+ assert_eq ! ( WrappedF32 ( y) . round_ties_even( ) . 0 , y. round_ties_even( ) ) ;
2777+ }
2778+ }
2779+ }
25132780}
0 commit comments