@@ -796,6 +796,49 @@ pub trait FloatCore: Num + NumCast + Neg<Output = Self> + PartialOrd + Copy {
796
796
/// check(f64::NEG_INFINITY, 1 << 52, 972, -1);
797
797
/// ```
798
798
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
+ }
799
842
}
800
843
801
844
impl FloatCore for f32 {
@@ -844,6 +887,11 @@ impl FloatCore for f32 {
844
887
Self :: powi( self , n: i32 ) -> Self ;
845
888
}
846
889
890
+ #[ cfg( all( feature = "std" , has_round_ties_even) ) ]
891
+ forward ! {
892
+ Self :: round_ties_even( self ) -> Self ;
893
+ }
894
+
847
895
#[ cfg( all( not( feature = "std" ) , feature = "libm" ) ) ]
848
896
forward ! {
849
897
libm:: floorf as floor( self ) -> Self ;
@@ -906,6 +954,11 @@ impl FloatCore for f64 {
906
954
Self :: powi( self , n: i32 ) -> Self ;
907
955
}
908
956
957
+ #[ cfg( all( feature = "std" , has_round_ties_even) ) ]
958
+ forward ! {
959
+ Self :: round_ties_even( self ) -> Self ;
960
+ }
961
+
909
962
#[ cfg( all( not( feature = "std" ) , feature = "libm" ) ) ]
910
963
forward ! {
911
964
libm:: floor as floor( self ) -> Self ;
@@ -1909,6 +1962,49 @@ pub trait Float: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
1909
1962
self . neg ( )
1910
1963
}
1911
1964
}
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
+ }
1912
2008
}
1913
2009
1914
2010
#[ cfg( feature = "std" ) ]
@@ -1989,6 +2085,11 @@ macro_rules! float_impl_std {
1989
2085
Self :: atanh( self ) -> Self ;
1990
2086
Self :: copysign( self , sign: Self ) -> Self ;
1991
2087
}
2088
+
2089
+ #[ cfg( has_round_ties_even) ]
2090
+ forward! {
2091
+ Self :: round_ties_even( self ) -> Self ;
2092
+ }
1992
2093
}
1993
2094
} ;
1994
2095
}
@@ -2510,4 +2611,170 @@ mod tests {
2510
2611
check_lt ( f32:: INFINITY , f32:: NAN ) ;
2511
2612
check_gt ( f32:: NAN , 1.0_f32 ) ;
2512
2613
}
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
+ }
2513
2780
}
0 commit comments