@@ -36,7 +36,7 @@ use std::str::FromStr;
36
36
use bigint:: { BigInt , BigUint , Sign } ;
37
37
38
38
use integer:: Integer ;
39
- use traits:: { FromPrimitive , Float , PrimInt , Num , Signed , Zero , One } ;
39
+ use traits:: { FromPrimitive , Float , PrimInt , Num , Signed , Zero , One , Bounded , NumCast } ;
40
40
41
41
/// Represents the ratio between 2 numbers.
42
42
#[ derive( Copy , Clone , Hash , Debug ) ]
@@ -668,6 +668,179 @@ impl RatioErrorKind {
668
668
}
669
669
}
670
670
671
+ impl FromPrimitive for Ratio < BigInt > {
672
+ fn from_i64 ( n : i64 ) -> Option < Self > {
673
+ Some ( Ratio :: from_integer ( n. into ( ) ) )
674
+ }
675
+
676
+ fn from_u64 ( n : u64 ) -> Option < Self > {
677
+ Some ( Ratio :: from_integer ( n. into ( ) ) )
678
+ }
679
+
680
+ fn from_f32 ( n : f32 ) -> Option < Self > {
681
+ Ratio :: from_float ( n)
682
+ }
683
+
684
+ fn from_f64 ( n : f64 ) -> Option < Self > {
685
+ Ratio :: from_float ( n)
686
+ }
687
+ }
688
+
689
+ macro_rules! from_primitive_integer {
690
+ ( $typ: ty, $approx: ident) => {
691
+ impl FromPrimitive for Ratio <$typ> {
692
+ fn from_i64( n: i64 ) -> Option <Self > {
693
+ <$typ as FromPrimitive >:: from_i64( n) . map( Ratio :: from_integer)
694
+ }
695
+
696
+ fn from_u64( n: u64 ) -> Option <Self > {
697
+ <$typ as FromPrimitive >:: from_u64( n) . map( Ratio :: from_integer)
698
+ }
699
+
700
+ fn from_f32( n: f32 ) -> Option <Self > {
701
+ $approx( n, 10e-20 , 30 )
702
+ }
703
+
704
+ fn from_f64( n: f64 ) -> Option <Self > {
705
+ $approx( n, 10e-20 , 30 )
706
+ }
707
+ }
708
+ }
709
+ }
710
+
711
+ from_primitive_integer ! ( i8 , approximate_float) ;
712
+ from_primitive_integer ! ( i16 , approximate_float) ;
713
+ from_primitive_integer ! ( i32 , approximate_float) ;
714
+ from_primitive_integer ! ( i64 , approximate_float) ;
715
+ from_primitive_integer ! ( isize , approximate_float) ;
716
+
717
+ from_primitive_integer ! ( u8 , approximate_float_unsigned) ;
718
+ from_primitive_integer ! ( u16 , approximate_float_unsigned) ;
719
+ from_primitive_integer ! ( u32 , approximate_float_unsigned) ;
720
+ from_primitive_integer ! ( u64 , approximate_float_unsigned) ;
721
+ from_primitive_integer ! ( usize , approximate_float_unsigned) ;
722
+
723
+ impl < T : Integer + Signed + Bounded + NumCast + Clone > Ratio < T > {
724
+ pub fn approximate_float < F : Float + NumCast > ( f : F ) -> Option < Ratio < T > > {
725
+ // 1/10e-20 < 1/2**32 which seems like a good default, and 30 seems
726
+ // to work well. Might want to choose something based on the types in the future, e.g.
727
+ // T::max().recip() and T::bits() or something similar.
728
+ let epsilon = <F as NumCast >:: from ( 10e-20 ) . expect ( "Can't convert 10e-20" ) ;
729
+ approximate_float ( f, epsilon, 30 )
730
+ }
731
+ }
732
+
733
+ fn approximate_float < T , F > ( val : F , max_error : F , max_iterations : usize ) -> Option < Ratio < T > >
734
+ where T : Integer + Signed + Bounded + NumCast + Clone ,
735
+ F : Float + NumCast
736
+ {
737
+ let negative = val. is_sign_negative ( ) ;
738
+ let abs_val = val. abs ( ) ;
739
+
740
+ let r = approximate_float_unsigned ( abs_val, max_error, max_iterations) ;
741
+
742
+ // Make negative again if needed
743
+ if negative {
744
+ r. map ( |r| r. neg ( ) )
745
+ } else {
746
+ r
747
+ }
748
+ }
749
+
750
+ // No Unsigned constraint because this also works on positive integers and is called
751
+ // like that, see above
752
+ fn approximate_float_unsigned < T , F > ( val : F , max_error : F , max_iterations : usize ) -> Option < Ratio < T > >
753
+ where T : Integer + Bounded + NumCast + Clone ,
754
+ F : Float + NumCast
755
+ {
756
+ // Continued fractions algorithm
757
+ // http://mathforum.org/dr.math/faq/faq.fractions.html#decfrac
758
+
759
+ if val < F :: zero ( ) {
760
+ return None ;
761
+ }
762
+
763
+ let mut q = val;
764
+ let mut n0 = T :: zero ( ) ;
765
+ let mut d0 = T :: one ( ) ;
766
+ let mut n1 = T :: one ( ) ;
767
+ let mut d1 = T :: zero ( ) ;
768
+
769
+ let t_max = T :: max_value ( ) ;
770
+ let t_max_f = match <F as NumCast >:: from ( t_max. clone ( ) ) {
771
+ None => return None ,
772
+ Some ( t_max_f) => t_max_f,
773
+ } ;
774
+
775
+ // 1/epsilon > T::MAX
776
+ let epsilon = t_max_f. recip ( ) ;
777
+
778
+ // Overflow
779
+ if q > t_max_f {
780
+ return None ;
781
+ }
782
+
783
+ for _ in 0 ..max_iterations {
784
+ let a = match <T as NumCast >:: from ( q) {
785
+ None => break ,
786
+ Some ( a) => a,
787
+ } ;
788
+
789
+ let a_f = match <F as NumCast >:: from ( a. clone ( ) ) {
790
+ None => break ,
791
+ Some ( a_f) => a_f,
792
+ } ;
793
+ let f = q - a_f;
794
+
795
+ // Prevent overflow
796
+ if !a. is_zero ( ) &&
797
+ ( n1 > t_max. clone ( ) / a. clone ( ) ||
798
+ d1 > t_max. clone ( ) / a. clone ( ) ||
799
+ a. clone ( ) * n1. clone ( ) > t_max. clone ( ) - n0. clone ( ) ||
800
+ a. clone ( ) * d1. clone ( ) > t_max. clone ( ) - d0. clone ( ) ) {
801
+ break ;
802
+ }
803
+
804
+ let n = a. clone ( ) * n1. clone ( ) + n0. clone ( ) ;
805
+ let d = a. clone ( ) * d1. clone ( ) + d0. clone ( ) ;
806
+
807
+ n0 = n1;
808
+ d0 = d1;
809
+ n1 = n. clone ( ) ;
810
+ d1 = d. clone ( ) ;
811
+
812
+ // Simplify fraction. Doing so here instead of at the end
813
+ // allows us to get closer to the target value without overflows
814
+ let g = Integer :: gcd ( & n1, & d1) ;
815
+ if !g. is_zero ( ) {
816
+ n1 = n1 / g. clone ( ) ;
817
+ d1 = d1 / g. clone ( ) ;
818
+ }
819
+
820
+ // Close enough?
821
+ let ( n_f, d_f) = match ( <F as NumCast >:: from ( n) , <F as NumCast >:: from ( d) ) {
822
+ ( Some ( n_f) , Some ( d_f) ) => ( n_f, d_f) ,
823
+ _ => break ,
824
+ } ;
825
+ if ( n_f / d_f - val) . abs ( ) < max_error {
826
+ break ;
827
+ }
828
+
829
+ // Prevent division by ~0
830
+ if f < epsilon {
831
+ break ;
832
+ }
833
+ q = f. recip ( ) ;
834
+ }
835
+
836
+ // Overflow
837
+ if d1. is_zero ( ) {
838
+ return None ;
839
+ }
840
+
841
+ Some ( Ratio :: new ( n1, d1) )
842
+ }
843
+
671
844
#[ cfg( test) ]
672
845
fn hash < T : hash:: Hash > ( x : & T ) -> u64 {
673
846
use std:: hash:: Hasher ;
@@ -684,6 +857,7 @@ mod test {
684
857
685
858
use std:: str:: FromStr ;
686
859
use std:: i32;
860
+ use std:: f64;
687
861
use traits:: { Zero , One , Signed , FromPrimitive , Float } ;
688
862
689
863
pub const _0: Rational = Ratio {
@@ -774,6 +948,38 @@ mod test {
774
948
let _a = Ratio :: new ( 1 , 0 ) ;
775
949
}
776
950
951
+ #[ test]
952
+ fn test_approximate_float ( ) {
953
+ assert_eq ! ( Ratio :: from_f32( 0.5f32 ) , Some ( Ratio :: new( 1i64 , 2 ) ) ) ;
954
+ assert_eq ! ( Ratio :: from_f64( 0.5f64 ) , Some ( Ratio :: new( 1i32 , 2 ) ) ) ;
955
+ assert_eq ! ( Ratio :: from_f32( 5f32 ) , Some ( Ratio :: new( 5i64 , 1 ) ) ) ;
956
+ assert_eq ! ( Ratio :: from_f64( 5f64 ) , Some ( Ratio :: new( 5i32 , 1 ) ) ) ;
957
+ assert_eq ! ( Ratio :: from_f32( 29.97f32 ) , Some ( Ratio :: new( 2997i64 , 100 ) ) ) ;
958
+ assert_eq ! ( Ratio :: from_f32( -29.97f32 ) , Some ( Ratio :: new( -2997i64 , 100 ) ) ) ;
959
+
960
+ assert_eq ! ( Ratio :: <i8 >:: from_f32( 63.5f32 ) , Some ( Ratio :: new( 127i8 , 2 ) ) ) ;
961
+ assert_eq ! ( Ratio :: <i8 >:: from_f32( 126.5f32 ) , Some ( Ratio :: new( 126i8 , 1 ) ) ) ;
962
+ assert_eq ! ( Ratio :: <i8 >:: from_f32( 127.0f32 ) , Some ( Ratio :: new( 127i8 , 1 ) ) ) ;
963
+ assert_eq ! ( Ratio :: <i8 >:: from_f32( 127.5f32 ) , None ) ;
964
+ assert_eq ! ( Ratio :: <i8 >:: from_f32( -63.5f32 ) , Some ( Ratio :: new( -127i8 , 2 ) ) ) ;
965
+ assert_eq ! ( Ratio :: <i8 >:: from_f32( -126.5f32 ) , Some ( Ratio :: new( -126i8 , 1 ) ) ) ;
966
+ assert_eq ! ( Ratio :: <i8 >:: from_f32( -127.0f32 ) , Some ( Ratio :: new( -127i8 , 1 ) ) ) ;
967
+ assert_eq ! ( Ratio :: <i8 >:: from_f32( -127.5f32 ) , None ) ;
968
+
969
+ assert_eq ! ( Ratio :: <u8 >:: from_f32( -127f32 ) , None ) ;
970
+ assert_eq ! ( Ratio :: <u8 >:: from_f32( 127f32 ) , Some ( Ratio :: new( 127u8 , 1 ) ) ) ;
971
+ assert_eq ! ( Ratio :: <u8 >:: from_f32( 127.5f32 ) , Some ( Ratio :: new( 255u8 , 2 ) ) ) ;
972
+ assert_eq ! ( Ratio :: <u8 >:: from_f32( 256f32 ) , None ) ;
973
+
974
+ assert_eq ! ( Ratio :: <i64 >:: from_f64( -10e200 ) , None ) ;
975
+ assert_eq ! ( Ratio :: <i64 >:: from_f64( 10e200 ) , None ) ;
976
+ assert_eq ! ( Ratio :: <i64 >:: from_f64( f64 :: INFINITY ) , None ) ;
977
+ assert_eq ! ( Ratio :: <i64 >:: from_f64( f64 :: NEG_INFINITY ) , None ) ;
978
+ assert_eq ! ( Ratio :: <i64 >:: from_f64( f64 :: NAN ) , None ) ;
979
+ assert_eq ! ( Ratio :: <i64 >:: from_f64( f64 :: EPSILON ) , Some ( Ratio :: new( 1 , 4503599627370496 ) ) ) ;
980
+ assert_eq ! ( Ratio :: <i64 >:: from_f64( 0.0 ) , Some ( Ratio :: new( 0 , 1 ) ) ) ;
981
+ assert_eq ! ( Ratio :: <i64 >:: from_f64( -0.0 ) , Some ( Ratio :: new( 0 , 1 ) ) ) ;
982
+ }
777
983
778
984
#[ test]
779
985
fn test_cmp ( ) {
0 commit comments