@@ -28,16 +28,17 @@ use core::cmp;
2828use core:: fmt;
2929use core:: fmt:: { Binary , Display , Formatter , LowerExp , LowerHex , Octal , UpperExp , UpperHex } ;
3030use core:: hash:: { Hash , Hasher } ;
31- use core:: ops:: { Add , Div , Mul , Neg , Rem , Sub } ;
31+ use core:: ops:: { Add , Div , Mul , Neg , Rem , ShlAssign , Sub } ;
3232use core:: str:: FromStr ;
3333#[ cfg( feature = "std" ) ]
3434use std:: error:: Error ;
3535
3636#[ cfg( feature = "bigint" ) ]
37- use num_bigint:: { BigInt , BigUint , Sign } ;
37+ use num_bigint:: { BigInt , BigUint , Sign , ToBigInt } ;
3838
3939use num_integer:: Integer ;
4040use num_traits:: float:: FloatCore ;
41+ use num_traits:: ToPrimitive ;
4142use num_traits:: {
4243 Bounded , CheckedAdd , CheckedDiv , CheckedMul , CheckedSub , FromPrimitive , Inv , Num , NumCast , One ,
4344 Pow , Signed , Zero ,
@@ -1360,6 +1361,223 @@ where
13601361 Some ( Ratio :: new ( n1, d1) )
13611362}
13621363
1364+ #[ cfg( not( feature = "bigint" ) ) ]
1365+ macro_rules! to_primitive_small {
1366+ ( $( $type_name: ty) * ) => ( $(
1367+ impl ToPrimitive for Ratio <$type_name> {
1368+ fn to_i64( & self ) -> Option <i64 > {
1369+ self . to_integer( ) . to_i64( )
1370+ }
1371+
1372+ fn to_i128( & self ) -> Option <i128 > {
1373+ self . to_integer( ) . to_i128( )
1374+ }
1375+
1376+ fn to_u64( & self ) -> Option <u64 > {
1377+ self . to_integer( ) . to_u64( )
1378+ }
1379+
1380+ fn to_u128( & self ) -> Option <u128 > {
1381+ self . to_integer( ) . to_u128( )
1382+ }
1383+
1384+ fn to_f64( & self ) -> Option <f64 > {
1385+ Some ( self . numer. to_f64( ) . unwrap( ) / self . denom. to_f64( ) . unwrap( ) )
1386+ }
1387+ }
1388+ ) * )
1389+ }
1390+
1391+ #[ cfg( not( feature = "bigint" ) ) ]
1392+ to_primitive_small ! ( u8 i8 u16 i16 u32 i32 ) ;
1393+
1394+ #[ cfg( all( target_pointer_width = "32" , not( feature = "bigint" ) ) ) ]
1395+ to_primitive_small ! ( usize isize ) ;
1396+
1397+ #[ cfg( not( feature = "bigint" ) ) ]
1398+ macro_rules! to_primitive_64 {
1399+ ( $( $type_name: ty) * ) => ( $(
1400+ impl ToPrimitive for Ratio <$type_name> {
1401+ fn to_i64( & self ) -> Option <i64 > {
1402+ self . to_integer( ) . to_i64( )
1403+ }
1404+
1405+ fn to_i128( & self ) -> Option <i128 > {
1406+ self . to_integer( ) . to_i128( )
1407+ }
1408+
1409+ fn to_u64( & self ) -> Option <u64 > {
1410+ self . to_integer( ) . to_u64( )
1411+ }
1412+
1413+ fn to_u128( & self ) -> Option <u128 > {
1414+ self . to_integer( ) . to_u128( )
1415+ }
1416+
1417+ fn to_f64( & self ) -> Option <f64 > {
1418+ Some ( ratio_to_f64(
1419+ self . numer as i128 ,
1420+ self . denom as i128
1421+ ) )
1422+ }
1423+ }
1424+ ) * )
1425+ }
1426+
1427+ #[ cfg( not( feature = "bigint" ) ) ]
1428+ to_primitive_64 ! ( u64 i64 ) ;
1429+
1430+ #[ cfg( all( target_pointer_width = "64" , not( feature = "bigint" ) ) ) ]
1431+ to_primitive_64 ! ( usize isize ) ;
1432+
1433+ #[ cfg( feature = "bigint" ) ]
1434+ impl < T : Clone + Integer + ToPrimitive + ToBigInt > ToPrimitive for Ratio < T > {
1435+ fn to_i64 ( & self ) -> Option < i64 > {
1436+ self . to_integer ( ) . to_i64 ( )
1437+ }
1438+
1439+ fn to_i128 ( & self ) -> Option < i128 > {
1440+ self . to_integer ( ) . to_i128 ( )
1441+ }
1442+
1443+ fn to_u64 ( & self ) -> Option < u64 > {
1444+ self . to_integer ( ) . to_u64 ( )
1445+ }
1446+
1447+ fn to_u128 ( & self ) -> Option < u128 > {
1448+ self . to_integer ( ) . to_u128 ( )
1449+ }
1450+
1451+ fn to_f64 ( & self ) -> Option < f64 > {
1452+ match ( self . numer . to_i64 ( ) , self . denom . to_i64 ( ) ) {
1453+ ( Some ( numer) , Some ( denom) ) => Some ( ratio_to_f64 (
1454+ <i128 as From < _ > >:: from ( numer) ,
1455+ <i128 as From < _ > >:: from ( denom) ,
1456+ ) ) ,
1457+ _ => {
1458+ let numer: BigInt = self . numer . to_bigint ( ) ?;
1459+ let denom: BigInt = self . denom . to_bigint ( ) ?;
1460+ Some ( ratio_to_f64 ( numer, denom) )
1461+ }
1462+ }
1463+ }
1464+ }
1465+
1466+ trait Bits {
1467+ fn bits ( & self ) -> usize ;
1468+ }
1469+
1470+ #[ cfg( feature = "bigint" ) ]
1471+ impl Bits for BigInt {
1472+ fn bits ( & self ) -> usize {
1473+ self . bits ( )
1474+ }
1475+ }
1476+
1477+ impl Bits for i128 {
1478+ fn bits ( & self ) -> usize {
1479+ ( 128 - self . wrapping_abs ( ) . leading_zeros ( ) ) as usize
1480+ }
1481+ }
1482+
1483+ /// Converts a ratio of `T` to an f64.
1484+ ///
1485+ /// In addition to stated trait bounds, `T` must be able to hold numbers 56 bits larger than
1486+ /// the largest of `numer` and `denom`. This is automatically true if `T` is `BigInt`.
1487+ fn ratio_to_f64 < T : Bits + Clone + Integer + Signed + ShlAssign < usize > + ToPrimitive > (
1488+ numer : T ,
1489+ denom : T ,
1490+ ) -> f64 {
1491+ assert_eq ! (
1492+ core:: f64 :: RADIX ,
1493+ 2 ,
1494+ "only floating point implementations with radix 2 are supported"
1495+ ) ;
1496+
1497+ // Inclusive upper and lower bounds to the range of exactly-representable ints in an f64.
1498+ const MAX_EXACT_INT : i64 = 1i64 << core:: f64:: MANTISSA_DIGITS ;
1499+ const MIN_EXACT_INT : i64 = -MAX_EXACT_INT ;
1500+
1501+ let flo_sign = numer. signum ( ) . to_f64 ( ) . unwrap ( ) * denom. signum ( ) . to_f64 ( ) . unwrap ( ) ;
1502+
1503+ if numer. is_zero ( ) {
1504+ return 0.0 * flo_sign;
1505+ }
1506+
1507+ // Fast track: both sides can losslessly be converted to f64s. In this case, letting the
1508+ // FPU do the job is faster and easier. In any other case, converting to f64s may lead
1509+ // to an inexact result: https://stackoverflow.com/questions/56641441/.
1510+ if let ( Some ( n) , Some ( d) ) = ( numer. to_i64 ( ) , denom. to_i64 ( ) ) {
1511+ if MIN_EXACT_INT <= n && n <= MAX_EXACT_INT && MIN_EXACT_INT <= d && d <= MAX_EXACT_INT {
1512+ return n. to_f64 ( ) . unwrap ( ) / d. to_f64 ( ) . unwrap ( ) ;
1513+ }
1514+ }
1515+
1516+ // Otherwise, the goal is to obtain a quotient with at least 55 bits. 53 of these bits will
1517+ // be used as the mantissa of the resulting float, and the remaining two are for rounding.
1518+ // There's an error of up to 1 on the number of resulting bits, so we may get either 55 or
1519+ // 56 bits.
1520+ let mut numer = numer. abs ( ) ;
1521+ let mut denom = denom. abs ( ) ;
1522+ let ( is_diff_positive, absolute_diff) = match numer. bits ( ) . checked_sub ( denom. bits ( ) ) {
1523+ Some ( diff) => ( true , diff) ,
1524+ None => ( false , denom. bits ( ) - numer. bits ( ) ) ,
1525+ } ;
1526+
1527+ // Filter out overflows and underflows. After this step, the signed difference fits in an
1528+ // isize.
1529+ if is_diff_positive && absolute_diff > core:: f64:: MAX_EXP as usize {
1530+ return core:: f64:: INFINITY * flo_sign;
1531+ }
1532+ if !is_diff_positive
1533+ && absolute_diff > -core:: f64:: MIN_EXP as usize + core:: f64:: MANTISSA_DIGITS as usize + 1
1534+ {
1535+ return 0.0 * flo_sign;
1536+ }
1537+ let diff = if is_diff_positive {
1538+ absolute_diff. to_isize ( ) . unwrap ( )
1539+ } else {
1540+ -absolute_diff. to_isize ( ) . unwrap ( )
1541+ } ;
1542+
1543+ // Shift is chosen so that the quotient will have 55 or 56 bits. The exception is if the
1544+ // quotient is going to be subnormal, in which case it may have fewer bits.
1545+ let shift: isize =
1546+ diff. max ( core:: f64:: MIN_EXP as isize ) - core:: f64:: MANTISSA_DIGITS as isize - 2 ;
1547+ if shift >= 0 {
1548+ denom <<= shift as usize
1549+ } else {
1550+ numer <<= -shift as usize
1551+ } ;
1552+
1553+ let ( quotient, remainder) = numer. div_rem ( & denom) ;
1554+
1555+ // This is guaranteed to fit since we've set up quotient to be at most 56 bits.
1556+ let mut quotient = quotient. to_u64 ( ) . unwrap ( ) ;
1557+ let n_rounding_bits = {
1558+ let quotient_bits = 64 - quotient. leading_zeros ( ) as isize ;
1559+ let subnormal_bits = core:: f64:: MIN_EXP as isize - shift;
1560+ quotient_bits. max ( subnormal_bits) - core:: f64:: MANTISSA_DIGITS as isize
1561+ } as usize ;
1562+ debug_assert ! ( n_rounding_bits == 2 || n_rounding_bits == 3 ) ;
1563+ let rounding_bit_mask = ( 1u64 << n_rounding_bits) - 1 ;
1564+
1565+ // Round to 53 bits with round-to-even. For rounding, we need to take into account both
1566+ // our rounding bits and the division's remainder.
1567+ let ls_bit = quotient & ( 1u64 << n_rounding_bits) != 0 ;
1568+ let ms_rounding_bit = quotient & ( 1u64 << ( n_rounding_bits - 1 ) ) != 0 ;
1569+ let ls_rounding_bits = quotient & ( rounding_bit_mask >> 1 ) != 0 ;
1570+ if ms_rounding_bit && ( ls_bit || ls_rounding_bits || !remainder. is_zero ( ) ) {
1571+ quotient += 1u64 << n_rounding_bits;
1572+ }
1573+ quotient &= !rounding_bit_mask;
1574+
1575+ // The quotient is guaranteed to be exactly representable as it's now 53 bits + 2 or 3
1576+ // trailing zeros, so there is no risk of a rounding error here.
1577+ let q_float = quotient as f64 ;
1578+ q_float * 2f64 . powi ( shift as i32 ) * flo_sign
1579+ }
1580+
13631581#[ cfg( test) ]
13641582#[ cfg( feature = "std" ) ]
13651583fn hash < T : Hash > ( x : & T ) -> u64 {
@@ -1372,6 +1590,8 @@ fn hash<T: Hash>(x: &T) -> u64 {
13721590
13731591#[ cfg( test) ]
13741592mod test {
1593+ #[ cfg( all( feature = "bigint" ) ) ]
1594+ use super :: BigInt ;
13751595 #[ cfg( feature = "bigint" ) ]
13761596 use super :: BigRational ;
13771597 use super :: { Ratio , Rational , Rational64 } ;
@@ -1381,6 +1601,7 @@ mod test {
13811601 use core:: isize;
13821602 use core:: str:: FromStr ;
13831603 use num_integer:: Integer ;
1604+ use num_traits:: ToPrimitive ;
13841605 use num_traits:: { FromPrimitive , One , Pow , Signed , Zero } ;
13851606
13861607 pub const _0: Rational = Ratio { numer : 0 , denom : 1 } ;
@@ -2640,4 +2861,76 @@ mod test {
26402861 assert_eq ! ( r. numer( ) , & ( 123 / 3 ) ) ;
26412862 assert_eq ! ( r. denom( ) , & ( 456 / 3 ) ) ;
26422863 }
2864+
2865+ #[ test]
2866+ fn test_ratio_to_i64 ( ) {
2867+ assert_eq ! ( 5 , Rational64 :: new( 70 , 14 ) . to_u64( ) . unwrap( ) ) ;
2868+ assert_eq ! ( -3 , Rational64 :: new( -31 , 8 ) . to_i64( ) . unwrap( ) ) ;
2869+ assert_eq ! ( None , Rational64 :: new( -31 , 8 ) . to_u64( ) ) ;
2870+ }
2871+
2872+ #[ test]
2873+ #[ cfg( feature = "bigint" ) ]
2874+ fn test_ratio_to_i128 ( ) {
2875+ assert_eq ! (
2876+ 1i128 << 70 ,
2877+ Ratio :: <i128 >:: new( 1i128 << 77 , 1i128 << 7 )
2878+ . to_i128( )
2879+ . unwrap( )
2880+ ) ;
2881+ }
2882+
2883+ #[ test]
2884+ #[ cfg( feature = "bigint" ) ]
2885+ fn test_big_ratio_to_f64 ( ) {
2886+ assert_eq ! (
2887+ BigRational :: new(
2888+ "1234567890987654321234567890987654321234567890"
2889+ . parse( )
2890+ . unwrap( ) ,
2891+ "3" . parse( ) . unwrap( )
2892+ )
2893+ . to_f64( )
2894+ . unwrap( ) ,
2895+ 411522630329218100000000000000000000000000000f64
2896+ ) ;
2897+ assert_eq ! (
2898+ BigRational :: new( 1 . into( ) , BigInt :: one( ) << 1050 , )
2899+ . to_f64( )
2900+ . unwrap( ) ,
2901+ 0f64
2902+ ) ;
2903+ assert_eq ! (
2904+ BigRational :: new(
2905+ "1234567890987654321234567890" . parse( ) . unwrap( ) ,
2906+ "987654321234567890987654321" . parse( ) . unwrap( )
2907+ )
2908+ . to_f64( )
2909+ . unwrap( ) ,
2910+ 1.2499999893125f64
2911+ ) ;
2912+ }
2913+
2914+ #[ test]
2915+ fn test_ratio_to_f64 ( ) {
2916+ assert_eq ! ( 0.5f64 , Ratio :: <u8 >:: new( 1 , 2 ) . to_f64( ) . unwrap( ) ) ;
2917+ assert_eq ! ( 0.5f64 , Rational64 :: new( 1 , 2 ) . to_f64( ) . unwrap( ) ) ;
2918+ assert_eq ! ( -0.5f64 , Rational64 :: new( 1 , -2 ) . to_f64( ) . unwrap( ) ) ;
2919+ assert_eq ! ( 0.0f64 , Rational64 :: new( 0 , 2 ) . to_f64( ) . unwrap( ) ) ;
2920+ assert_eq ! ( -0.0f64 , Rational64 :: new( 0 , -2 ) . to_f64( ) . unwrap( ) ) ;
2921+ assert_eq ! (
2922+ 8f64 ,
2923+ Rational64 :: new( ( 1 << 57 ) + 1 , 1 << 54 ) . to_f64( ) . unwrap( )
2924+ ) ;
2925+ assert_eq ! (
2926+ 1.0000000000000002f64 ,
2927+ Rational64 :: new( ( 1 << 52 ) + 1 , 1 << 52 ) . to_f64( ) . unwrap( )
2928+ ) ;
2929+ assert_eq ! (
2930+ 1.0000000000000002f64 ,
2931+ Rational64 :: new( ( 1 << 60 ) + ( 1 << 8 ) , 1 << 60 )
2932+ . to_f64( )
2933+ . unwrap( )
2934+ ) ;
2935+ }
26432936}
0 commit comments