Переглянути джерело

Auto merge of #147 - rust-lang-nursery:conv, r=japaric

Conversion from&to float<->integer

this is a rebased version of #139

cc @ithinuel
bors 8 роки тому
батько
коміт
28ac4908f8

+ 14 - 0
compiler-rt/compiler-rt-cdylib/build.rs

@@ -62,6 +62,20 @@ fn main() {
         "powisf2.c",
         "subdf3.c",
         "subsf3.c",
+        "floatsisf.c",
+        "floatsidf.c",
+        "floatdidf.c",
+        "floatunsisf.c",
+        "floatunsidf.c",
+        "floatundidf.c",
+        "fixsfsi.c",
+        "fixsfdi.c",
+        "fixdfsi.c",
+        "fixdfdi.c",
+        "fixunssfsi.c",
+        "fixunssfdi.c",
+        "fixunsdfsi.c",
+        "fixunsdfdi.c",
         // 128 bit integers
         "lshrti3.c",
         "modti3.c",

+ 28 - 0
compiler-rt/compiler-rt-cdylib/src/lib.rs

@@ -26,6 +26,20 @@ extern {
     fn __powidf2();
     fn __subsf3();
     fn __subdf3();
+    fn __floatsisf();
+    fn __floatsidf();
+    fn __floatdidf();
+    fn __floatunsisf();
+    fn __floatunsidf();
+    fn __floatundidf();
+    fn __fixsfsi();
+    fn __fixsfdi();
+    fn __fixdfsi();
+    fn __fixdfdi();
+    fn __fixunssfsi();
+    fn __fixunssfdi();
+    fn __fixunsdfsi();
+    fn __fixunsdfdi();
 }
 
 macro_rules! declare {
@@ -61,6 +75,20 @@ declare!(___powisf2, __powisf2);
 declare!(___powidf2, __powidf2);
 declare!(___subsf3, __subsf3);
 declare!(___subdf3, __subdf3);
+declare!(___floatsisf, __floatsisf);
+declare!(___floatsidf, __floatsidf);
+declare!(___floatdidf, __floatdidf);
+declare!(___floatunsisf, __floatunsisf);
+declare!(___floatunsidf, __floatunsidf);
+declare!(___floatundidf, __floatundidf);
+declare!(___fixsfsi, __fixsfsi);
+declare!(___fixsfdi, __fixsfdi);
+declare!(___fixdfsi, __fixdfsi);
+declare!(___fixdfdi, __fixdfdi);
+declare!(___fixunssfsi, __fixunssfsi);
+declare!(___fixunssfdi, __fixunssfdi);
+declare!(___fixunsdfsi, __fixunsdfsi);
+declare!(___fixunsdfdi, __fixunsdfdi);
 
 #[cfg(all(not(windows),
           not(target_arch = "mips64"),

+ 6 - 0
src/arm.rs

@@ -112,6 +112,12 @@ pub extern "aapcs" fn __aeabi_uidiv(a: u32, b: u32) -> u32 {
     ::int::udiv::__udivsi3(a, b)
 }
 
+#[cfg(not(feature = "c"))]
+#[cfg_attr(not(test), no_mangle)]
+pub extern "C" fn __aeabi_ui2d(a: u32) -> f64 {
+    ::float::conv::__floatunsidf(a)
+}
+
 // TODO: These aeabi_* functions should be defined as aliases
 #[cfg(not(feature = "mem"))]
 extern "C" {

+ 266 - 0
src/float/conv.rs

@@ -0,0 +1,266 @@
+use float::Float;
+use int::Int;
+
+macro_rules! fp_overflow {
+    (infinity, $fty:ty, $sign: expr) => {
+        return {
+            <$fty as Float>::from_parts(
+                $sign,
+                <$fty as Float>::exponent_max() as <$fty as Float>::Int,
+                0 as <$fty as Float>::Int)
+        }
+    }
+}
+
+macro_rules! fp_convert {
+    ($intrinsic:ident: $ity:ty, $fty:ty) => {
+
+    pub extern "C" fn $intrinsic(i: $ity) -> $fty {
+        if i == 0 {
+            return 0.0
+        }
+
+        let mant_dig = <$fty>::significand_bits() + 1;
+        let exponent_bias = <$fty>::exponent_bias();
+
+        let n = <$ity>::bits();
+        let (s, a) = i.extract_sign();
+        let mut a = a;
+
+        // number of significant digits
+        let sd = n - a.leading_zeros();
+
+        // exponent
+        let mut e = sd - 1;
+
+        if <$ity>::bits() < mant_dig {
+            return <$fty>::from_parts(s,
+                (e + exponent_bias) as <$fty as Float>::Int,
+                (a as <$fty as Float>::Int) << (mant_dig - e - 1))
+        }
+
+        a = if sd > mant_dig {
+            /* start:  0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
+            *  finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
+            *                                                12345678901234567890123456
+            *  1 = msb 1 bit
+            *  P = bit MANT_DIG-1 bits to the right of 1
+            *  Q = bit MANT_DIG bits to the right of 1
+            *  R = "or" of all bits to the right of Q
+            */
+            let mant_dig_plus_one = mant_dig + 1;
+            let mant_dig_plus_two = mant_dig + 2;
+            a = if sd == mant_dig_plus_one {
+                a << 1
+            } else if sd == mant_dig_plus_two {
+                a
+            } else {
+                (a >> (sd - mant_dig_plus_two)) as <$ity as Int>::UnsignedInt |
+                ((a & <$ity as Int>::UnsignedInt::max_value()).wrapping_shl((n + mant_dig_plus_two) - sd) != 0) as <$ity as Int>::UnsignedInt
+            };
+
+            /* finish: */
+            a |= ((a & 4) != 0) as <$ity as Int>::UnsignedInt; /* Or P into R */
+            a += 1; /* round - this step may add a significant bit */
+            a >>= 2; /* dump Q and R */
+
+            /* a is now rounded to mant_dig or mant_dig+1 bits */
+            if (a & (1 << mant_dig)) != 0 {
+                a >>= 1; e += 1;
+            }
+            a
+            /* a is now rounded to mant_dig bits */
+        } else {
+            a.wrapping_shl(mant_dig - sd)
+            /* a is now rounded to mant_dig bits */
+        };
+
+        <$fty>::from_parts(s,
+            (e + exponent_bias) as <$fty as Float>::Int,
+            a as <$fty as Float>::Int)
+    }
+    }
+}
+
+fp_convert!(__floatsisf: i32, f32);
+fp_convert!(__floatsidf: i32, f64);
+fp_convert!(__floatdidf: i64, f64);
+fp_convert!(__floatunsisf: u32, f32);
+fp_convert!(__floatunsidf: u32, f64);
+fp_convert!(__floatundidf: u64, f64);
+
+#[derive(PartialEq, Debug)]
+enum Sign {
+    Positive,
+    Negative
+}
+macro_rules! fp_fix {
+    ($intrinsic:ident: $fty:ty, $ity:ty) => {
+        pub extern "C" fn $intrinsic(f: $fty) -> $ity {
+            let fixint_min = <$ity>::min_value();
+            let fixint_max = <$ity>::max_value();
+            let fixint_bits = <$ity>::bits() as usize;
+            let fixint_unsigned = fixint_min == 0;
+
+            let sign_bit = <$fty>::sign_mask();
+            let significand_bits = <$fty>::significand_bits() as usize;
+            let exponent_bias = <$fty>::exponent_bias() as usize;
+            //let exponent_max = <$fty>::exponent_max() as usize;
+
+            // Break a into sign, exponent, significand
+            let a_rep = <$fty>::repr(f);
+            let a_abs = a_rep & !sign_bit;
+
+            // this is used to work around -1 not being available for unsigned
+            let sign = if (a_rep & sign_bit) == 0 { Sign::Positive } else { Sign::Negative };
+            let mut exponent = (a_abs >> significand_bits) as usize;
+            let significand = (a_abs & <$fty>::significand_mask()) | <$fty>::implicit_bit();
+
+            // if < 1 or unsigned & negative
+            if  exponent < exponent_bias ||
+                fixint_unsigned && sign == Sign::Negative {
+                return 0
+            }
+            exponent -= exponent_bias;
+
+            // If the value is infinity, saturate.
+            // If the value is too large for the integer type, 0.
+            if exponent >= (if fixint_unsigned {fixint_bits} else {fixint_bits -1}) {
+                return if sign == Sign::Positive {fixint_max} else {fixint_min}
+            }
+            // If 0 <= exponent < significand_bits, right shift to get the result.
+            // Otherwise, shift left.
+            // (sign - 1) will never overflow as negative signs are already returned as 0 for unsigned
+            let r = if exponent < significand_bits {
+                (significand >> (significand_bits - exponent)) as $ity
+            } else {
+                (significand as $ity) << (exponent - significand_bits)
+            };
+
+            if sign == Sign::Negative {
+                (!r).wrapping_add(1)
+            } else {
+                r
+            }
+        }
+    }
+}
+
+fp_fix!(__fixsfsi: f32, i32);
+fp_fix!(__fixsfdi: f32, i64);
+fp_fix!(__fixdfsi: f64, i32);
+fp_fix!(__fixdfdi: f64, i64);
+
+fp_fix!(__fixunssfsi: f32, u32);
+fp_fix!(__fixunssfdi: f32, u64);
+fp_fix!(__fixunsdfsi: f64, u32);
+fp_fix!(__fixunsdfdi: f64, u64);
+
+// NOTE(cfg) for some reason, on arm*-unknown-linux-gnueabihf, our implementation doesn't
+// match the output of its gcc_s or compiler-rt counterpart. Until we investigate further, we'll
+// just avoid testing against them on those targets. Do note that our implementation gives the
+// correct answer; gcc_s and compiler-rt are incorrect in this case.
+//
+#[cfg(all(test, not(arm_linux)))]
+mod tests {
+    use qc::{I32, U32, I64, U64, F32, F64};
+
+    check! {
+        fn __floatsisf(f: extern "C" fn(i32) -> f32,
+                    a: I32)
+                    -> Option<F32> {
+            Some(F32(f(a.0)))
+        }
+        fn __floatsidf(f: extern "C" fn(i32) -> f64,
+                    a: I32)
+                    -> Option<F64> {
+            Some(F64(f(a.0)))
+        }
+        fn __floatdidf(f: extern "C" fn(i64) -> f64,
+                    a: I64)
+                    -> Option<F64> {
+            Some(F64(f(a.0)))
+        }
+        fn __floatunsisf(f: extern "C" fn(u32) -> f32,
+                    a: U32)
+                    -> Option<F32> {
+            Some(F32(f(a.0)))
+        }
+        fn __floatunsidf(f: extern "C" fn(u32) -> f64,
+                    a: U32)
+                    -> Option<F64> {
+            Some(F64(f(a.0)))
+        }
+        fn __floatundidf(f: extern "C" fn(u64) -> f64,
+                    a: U64)
+                    -> Option<F64> {
+            Some(F64(f(a.0)))
+        }
+
+        fn __fixsfsi(f: extern "C" fn(f32) -> i32,
+                    a: F32)
+                    -> Option<I32> {
+            if (a.0 as f64) > (i32::max_value() as f64) ||
+                (a.0 as f64) < (i32::min_value() as f64) || a.0.is_nan() {
+                   None
+           } else { Some(I32(f(a.0))) }
+        }
+        fn __fixsfdi(f: extern "C" fn(f32) -> i64,
+                    a: F32)
+                    -> Option<I64> {
+            if (a.0 as f64) > (i64::max_value() as f64) ||
+                (a.0 as f64) < (i64::min_value() as f64) || a.0.is_nan() {
+                   None
+           } else { Some(I64(f(a.0))) }
+        }
+        fn __fixdfsi(f: extern "C" fn(f64) -> i32,
+                    a: F64)
+                    -> Option<I32> {
+            if a.0 > (i32::max_value() as f64) ||
+               a.0 < (i32::min_value() as f64) || a.0.is_nan() {
+                   None
+           } else { Some(I32(f(a.0))) }
+        }
+        fn __fixdfdi(f: extern "C" fn(f64) -> i64,
+                    a: F64)
+                    -> Option<I64> {
+            if a.0 > (i64::max_value() as f64) ||
+               a.0 < (i64::min_value() as f64) || a.0.is_nan() {
+                   None
+           } else { Some(I64(f(a.0))) }
+        }
+
+        fn __fixunssfsi(f: extern "C" fn(f32) -> u32,
+                    a: F32)
+                    -> Option<U32> {
+            if (a.0 as f64) > (u32::max_value() as f64) ||
+                (a.0 as f64) < (u32::min_value() as f64) || a.0.is_nan() {
+                   None
+           } else { Some(U32(f(a.0))) }
+        }
+        fn __fixunssfdi(f: extern "C" fn(f32) -> u64,
+                    a: F32)
+                    -> Option<U64> {
+            if (a.0 as f64) > (u64::max_value() as f64) ||
+                (a.0 as f64) < (u64::min_value() as f64) || a.0.is_nan() {
+                   None
+           } else { Some(U64(f(a.0))) }
+        }
+        fn __fixunsdfsi(f: extern "C" fn(f64) -> u32,
+                    a: F64)
+                    -> Option<U32> {
+            if a.0 > (u32::max_value() as f64) ||
+               a.0 < (u32::min_value() as f64) || a.0.is_nan() {
+                   None
+           } else { Some(U32(f(a.0))) }
+        }
+        fn __fixunsdfdi(f: extern "C" fn(f64) -> u64,
+                    a: F64)
+                    -> Option<U64> {
+            if a.0 <= (u64::max_value() as f64) ||
+               a.0 >= (u64::min_value() as f64) || a.0.is_nan() {
+                   None
+           } else { Some(U64(f(a.0))) }
+        }
+    }
+}

+ 20 - 0
src/float/mod.rs

@@ -1,5 +1,6 @@
 use core::mem;
 
+pub mod conv;
 pub mod add;
 pub mod pow;
 pub mod sub;
@@ -19,6 +20,15 @@ pub trait Float: Sized + Copy {
     fn exponent_bits() -> u32 {
         Self::bits() - Self::significand_bits() - 1
     }
+    /// Returns the maximum value of the exponent
+    fn exponent_max() -> u32 {
+        (1 << Self::exponent_bits()) - 1
+    }
+
+    /// Returns the exponent bias value
+    fn exponent_bias() -> u32 {
+        Self::exponent_max() >> 1
+    }
 
     /// Returns a mask for the sign bit
     fn sign_mask() -> Self::Int;
@@ -26,6 +36,9 @@ pub trait Float: Sized + Copy {
     /// Returns a mask for the significand
     fn significand_mask() -> Self::Int;
 
+    // Returns the implicit bit of the float format
+    fn implicit_bit() -> Self::Int;
+
     /// Returns a mask for the exponent
     fn exponent_mask() -> Self::Int;
 
@@ -58,6 +71,9 @@ impl Float for f32 {
     fn significand_bits() -> u32 {
         23
     }
+    fn implicit_bit() -> Self::Int {
+        1 << Self::significand_bits()
+    }
     fn sign_mask() -> Self::Int {
         1 << (Self::bits() - 1)
     }
@@ -100,6 +116,10 @@ impl Float for f64 {
     fn significand_bits() -> u32 {
         52
     }
+    // Returns the implicit bit of the float format
+    fn implicit_bit() -> Self::Int {
+        1 << Self::significand_bits()
+    }
     fn sign_mask() -> Self::Int {
         1 << (Self::bits() - 1)
     }

+ 37 - 5
src/int/mod.rs

@@ -19,23 +19,55 @@ pub mod udiv;
 pub trait Int {
     /// Type with the same width but other signedness
     type OtherSign;
+    /// Unsigned version of Self
+    type UnsignedInt;
+
     /// Returns the bitwidth of the int type
     fn bits() -> u32;
+
+    /// Extracts the sign from self and returns a tuple.
+    ///
+    /// # Examples
+    ///
+    /// ```rust,ignore
+    /// let i = -25_i32;
+    /// let (sign, u) = i.extract_sign();
+    /// assert_eq!(sign, true);
+    /// assert_eq!(u, 25_u32);
+    /// ```
+    fn extract_sign(self) -> (bool, Self::UnsignedInt);
 }
 
 macro_rules! int_impl {
-    ($ity:ty, $sty:ty, $bits:expr) => {
-        impl Int for $ity {
-            type OtherSign = $sty;
+    ($ity:ty, $uty:ty, $bits:expr) => {
+        impl Int for $uty {
+            type OtherSign = $ity;
+            type UnsignedInt = $uty;
+
             fn bits() -> u32 {
                 $bits
             }
+
+            fn extract_sign(self) -> (bool, $uty) {
+                (false, self)
+            }
         }
-        impl Int for $sty {
-            type OtherSign = $ity;
+
+        impl Int for $ity {
+            type OtherSign = $uty;
+            type UnsignedInt = $uty;
+
             fn bits() -> u32 {
                 $bits
             }
+
+            fn extract_sign(self) -> (bool, $uty) {
+                if self < 0 {
+                    (true, !(self as $uty) + 1)
+                } else {
+                    (false, self as $uty)
+                }
+            }
         }
     }
 }

+ 2 - 2
src/qc.rs

@@ -15,7 +15,7 @@ use float::Float;
 // Generates values in the full range of the integer type
 macro_rules! arbitrary {
     ($TY:ident : $ty:ident) => {
-        #[derive(Clone, Copy)]
+        #[derive(Clone, Copy, PartialEq)]
         pub struct $TY(pub $ty);
 
         impl Arbitrary for $TY {
@@ -82,7 +82,7 @@ arbitrary!(U32: u32);
 // intrinsics.
 macro_rules! arbitrary_large {
     ($TY:ident : $ty:ident) => {
-        #[derive(Clone, Copy)]
+        #[derive(Clone, Copy, PartialEq)]
         pub struct $TY(pub $ty);
 
         impl Arbitrary for $TY {