diff options
Diffstat (limited to 'libcore/num')
| -rw-r--r-- | libcore/num/bignum.rs | 2 | ||||
| -rw-r--r-- | libcore/num/dec2flt/algorithm.rs | 89 | ||||
| -rw-r--r-- | libcore/num/int_macros.rs | 11 | ||||
| -rw-r--r-- | libcore/num/isize.rs | 2 | ||||
| -rw-r--r-- | libcore/num/mod.rs | 133 | ||||
| -rw-r--r-- | libcore/num/uint_macros.rs | 6 | ||||
| -rw-r--r-- | libcore/num/usize.rs | 2 | ||||
| -rw-r--r-- | libcore/num/wrapping.rs | 15 |
8 files changed, 234 insertions, 26 deletions
diff --git a/libcore/num/bignum.rs b/libcore/num/bignum.rs index 66c6deb..a881b53 100644 --- a/libcore/num/bignum.rs +++ b/libcore/num/bignum.rs @@ -33,7 +33,7 @@ use mem; use intrinsics; /// Arithmetic operations required by bignums. -pub trait FullOps { +pub trait FullOps: Sized { /// Returns `(carry', v')` such that `carry' * 2^W + v' = self + other + carry`, /// where `W` is the number of bits in `Self`. fn full_add(self, other: Self, carry: bool) -> (bool /*carry*/, Self); diff --git a/libcore/num/dec2flt/algorithm.rs b/libcore/num/dec2flt/algorithm.rs index e33c281..c7af46a 100644 --- a/libcore/num/dec2flt/algorithm.rs +++ b/libcore/num/dec2flt/algorithm.rs @@ -32,19 +32,80 @@ fn power_of_ten(e: i16) -> Fp { Fp { f: sig, e: exp } } +// In most architectures, floating point operations have an explicit bit size, therefore the +// precision of the computation is determined on a per-operation basis. +#[cfg(any(not(target_arch="x86"), target_feature="sse2"))] +mod fpu_precision { + pub fn set_precision<T>() { } +} + +// On x86, the x87 FPU is used for float operations if the SSE/SSE2 extensions are not available. +// The x87 FPU operates with 80 bits of precision by default, which means that operations will +// round to 80 bits causing double rounding to happen when values are eventually represented as +// 32/64 bit float values. To overcome this, the FPU control word can be set so that the +// computations are performed in the desired precision. +#[cfg(all(target_arch="x86", not(target_feature="sse2")))] +mod fpu_precision { + use mem::size_of; + use ops::Drop; + + /// A structure used to preserve the original value of the FPU control word, so that it can be + /// restored when the structure is dropped. + /// + /// The x87 FPU is a 16-bits register whose fields are as follows: + /// + /// | 12-15 | 10-11 | 8-9 | 6-7 | 5 | 4 | 3 | 2 | 1 | 0 | + /// |------:|------:|----:|----:|---:|---:|---:|---:|---:|---:| + /// | | RC | PC | | PM | UM | OM | ZM | DM | IM | + /// + /// The documentation for all of the fields is available in the IA-32 Architectures Software + /// Developer's Manual (Volume 1). + /// + /// The only field which is relevant for the following code is PC, Precision Control. This + /// field determines the precision of the operations performed by the FPU. It can be set to: + /// - 0b00, single precision i.e. 32-bits + /// - 0b10, double precision i.e. 64-bits + /// - 0b11, double extended precision i.e. 80-bits (default state) + /// The 0b01 value is reserved and should not be used. + pub struct FPUControlWord(u16); + + fn set_cw(cw: u16) { + unsafe { asm!("fldcw $0" :: "m" (cw) :: "volatile") } + } + + /// Set the precision field of the FPU to `T` and return a `FPUControlWord` + pub fn set_precision<T>() -> FPUControlWord { + let cw = 0u16; + + // Compute the value for the Precision Control field that is appropriate for `T`. + let cw_precision = match size_of::<T>() { + 4 => 0x0000, // 32 bits + 8 => 0x0200, // 64 bits + _ => 0x0300, // default, 80 bits + }; + + // Get the original value of the control word to restore it later, when the + // `FPUControlWord` structure is dropped + unsafe { asm!("fnstcw $0" : "=*m" (&cw) ::: "volatile") } + + // Set the control word to the desired precision. This is achieved by masking away the old + // precision (bits 8 and 9, 0x300) and replacing it with the precision flag computed above. + set_cw((cw & 0xFCFF) | cw_precision); + + FPUControlWord(cw) + } + + impl Drop for FPUControlWord { + fn drop(&mut self) { + set_cw(self.0) + } + } +} + /// The fast path of Bellerophon using machine-sized integers and floats. /// /// This is extracted into a separate function so that it can be attempted before constructing /// a bignum. -/// -/// The fast path crucially depends on arithmetic being correctly rounded, so on x86 -/// without SSE or SSE2 it will be **wrong** (as in, off by one ULP occasionally), because the x87 -/// FPU stack will round to 80 bit first before rounding to 64/32 bit. However, as such hardware -/// is extremely rare nowadays and in fact all in-tree target triples assume an SSE2-capable -/// microarchitecture, there is little incentive to deal with that. There's a test that will fail -/// when SSE or SSE2 is disabled, so people building their own non-SSE copy will get a heads up. -/// -/// FIXME: It would nevertheless be nice if we had a good way to detect and deal with x87. pub fn fast_path<T: RawFloat>(integral: &[u8], fractional: &[u8], e: i64) -> Option<T> { let num_digits = integral.len() + fractional.len(); // log_10(f64::max_sig) ~ 15.95. We compare the exact value to max_sig near the end, @@ -60,9 +121,17 @@ pub fn fast_path<T: RawFloat>(integral: &[u8], fractional: &[u8], e: i64) -> Opt if f > T::max_sig() { return None; } + + // The fast path crucially depends on arithmetic being rounded to the correct number of bits + // without any intermediate rounding. On x86 (without SSE or SSE2) this requires the precision + // of the x87 FPU stack to be changed so that it directly rounds to 64/32 bit. + // The `set_precision` function takes care of setting the precision on architectures which + // require setting it by changing the global state (like the control word of the x87 FPU). + let _cw = fpu_precision::set_precision::<T>(); + // The case e < 0 cannot be folded into the other branch. Negative powers result in // a repeating fractional part in binary, which are rounded, which causes real - // (and occasioally quite significant!) errors in the final result. + // (and occasionally quite significant!) errors in the final result. if e >= 0 { Some(T::from_int(f) * T::short_fast_pow10(e as usize)) } else { diff --git a/libcore/num/int_macros.rs b/libcore/num/int_macros.rs index 4234925..bd6cfc4 100644 --- a/libcore/num/int_macros.rs +++ b/libcore/num/int_macros.rs @@ -10,18 +10,13 @@ #![doc(hidden)] -macro_rules! int_module { ($T:ty, $bits:expr) => ( +macro_rules! int_module { ($T:ident, $bits:expr) => ( -// FIXME(#11621): Should be deprecated once CTFE is implemented in favour of -// calling the `Bounded::min_value` function. #[stable(feature = "rust1", since = "1.0.0")] #[allow(missing_docs)] -pub const MIN: $T = (-1 as $T) << ($bits - 1); -// FIXME(#9837): Compute MIN like this so the high bits that shouldn't exist are 0. -// FIXME(#11621): Should be deprecated once CTFE is implemented in favour of -// calling the `Bounded::max_value` function. +pub const MIN: $T = $T::min_value(); #[stable(feature = "rust1", since = "1.0.0")] #[allow(missing_docs)] -pub const MAX: $T = !MIN; +pub const MAX: $T = $T::max_value(); ) } diff --git a/libcore/num/isize.rs b/libcore/num/isize.rs index de5b177..86bcef4 100644 --- a/libcore/num/isize.rs +++ b/libcore/num/isize.rs @@ -14,6 +14,8 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[cfg(target_pointer_width = "16")] +int_module! { isize, 16 } #[cfg(target_pointer_width = "32")] int_module! { isize, 32 } #[cfg(target_pointer_width = "64")] diff --git a/libcore/num/mod.rs b/libcore/num/mod.rs index e5f0469..445e346 100644 --- a/libcore/num/mod.rs +++ b/libcore/num/mod.rs @@ -15,7 +15,7 @@ use char::CharExt; use cmp::PartialOrd; -use convert::From; +use convert::{From, TryFrom}; use fmt; use intrinsics; use marker::{Copy, Sized}; @@ -37,6 +37,17 @@ use slice::SliceExt; /// `wrapping_add`, or through the `Wrapping<T>` type, which says that /// all standard arithmetic operations on the underlying value are /// intended to have wrapping semantics. +/// +/// # Examples +/// +/// ``` +/// use std::num::Wrapping; +/// +/// let zero = Wrapping(0u32); +/// let one = Wrapping(1u32); +/// +/// assert_eq!(std::u32::MAX, (zero - one).0); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Hash)] pub struct Wrapping<T>(#[stable(feature = "rust1", since = "1.0.0")] pub T); @@ -1025,7 +1036,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - #[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD. + #[rustc_inherit_overflow_checks] pub fn pow(self, mut exp: u32) -> Self { let mut base = self; let mut acc = Self::one(); @@ -1067,7 +1078,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - #[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD. + #[rustc_inherit_overflow_checks] pub fn abs(self) -> Self { if self.is_negative() { // Note that the #[inline] above means that the overflow @@ -1168,6 +1179,15 @@ impl i64 { intrinsics::mul_with_overflow } } +#[cfg(target_pointer_width = "16")] +#[lang = "isize"] +impl isize { + int_impl! { i16, u16, 16, + intrinsics::add_with_overflow, + intrinsics::sub_with_overflow, + intrinsics::mul_with_overflow } +} + #[cfg(target_pointer_width = "32")] #[lang = "isize"] impl isize { @@ -2044,7 +2064,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - #[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD. + #[rustc_inherit_overflow_checks] pub fn pow(self, mut exp: u32) -> Self { let mut base = self; let mut acc = Self::one(); @@ -2180,6 +2200,18 @@ impl u64 { intrinsics::mul_with_overflow } } +#[cfg(target_pointer_width = "16")] +#[lang = "usize"] +impl usize { + uint_impl! { u16, 16, + intrinsics::ctpop, + intrinsics::ctlz, + intrinsics::cttz, + intrinsics::bswap, + intrinsics::add_with_overflow, + intrinsics::sub_with_overflow, + intrinsics::mul_with_overflow } +} #[cfg(target_pointer_width = "32")] #[lang = "usize"] impl usize { @@ -2345,9 +2377,101 @@ macro_rules! from_str_radix_int_impl { } from_str_radix_int_impl! { isize i8 i16 i32 i64 usize u8 u16 u32 u64 } +/// The error type returned when a checked integral type conversion fails. +#[unstable(feature = "try_from", issue = "33417")] +#[derive(Debug, Copy, Clone)] +pub struct TryFromIntError(()); + +impl TryFromIntError { + #[unstable(feature = "int_error_internals", + reason = "available through Error trait and this method should \ + not be exposed publicly", + issue = "0")] + #[doc(hidden)] + pub fn __description(&self) -> &str { + "out of range integral type conversion attempted" + } +} + +#[unstable(feature = "try_from", issue = "33417")] +impl fmt::Display for TryFromIntError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.__description().fmt(fmt) + } +} + +macro_rules! same_sign_from_int_impl { + ($storage:ty, $target:ty, $($source:ty),*) => {$( + #[stable(feature = "rust1", since = "1.0.0")] + impl TryFrom<$source> for $target { + type Err = TryFromIntError; + + fn try_from(u: $source) -> Result<$target, TryFromIntError> { + let min = <$target as FromStrRadixHelper>::min_value() as $storage; + let max = <$target as FromStrRadixHelper>::max_value() as $storage; + if u as $storage < min || u as $storage > max { + Err(TryFromIntError(())) + } else { + Ok(u as $target) + } + } + } + )*} +} + +same_sign_from_int_impl!(u64, u8, u8, u16, u32, u64, usize); +same_sign_from_int_impl!(i64, i8, i8, i16, i32, i64, isize); +same_sign_from_int_impl!(u64, u16, u8, u16, u32, u64, usize); +same_sign_from_int_impl!(i64, i16, i8, i16, i32, i64, isize); +same_sign_from_int_impl!(u64, u32, u8, u16, u32, u64, usize); +same_sign_from_int_impl!(i64, i32, i8, i16, i32, i64, isize); +same_sign_from_int_impl!(u64, u64, u8, u16, u32, u64, usize); +same_sign_from_int_impl!(i64, i64, i8, i16, i32, i64, isize); +same_sign_from_int_impl!(u64, usize, u8, u16, u32, u64, usize); +same_sign_from_int_impl!(i64, isize, i8, i16, i32, i64, isize); + +macro_rules! cross_sign_from_int_impl { + ($unsigned:ty, $($signed:ty),*) => {$( + #[stable(feature = "rust1", since = "1.0.0")] + impl TryFrom<$unsigned> for $signed { + type Err = TryFromIntError; + + fn try_from(u: $unsigned) -> Result<$signed, TryFromIntError> { + let max = <$signed as FromStrRadixHelper>::max_value() as u64; + if u as u64 > max { + Err(TryFromIntError(())) + } else { + Ok(u as $signed) + } + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl TryFrom<$signed> for $unsigned { + type Err = TryFromIntError; + + fn try_from(u: $signed) -> Result<$unsigned, TryFromIntError> { + let max = <$unsigned as FromStrRadixHelper>::max_value() as u64; + if u < 0 || u as u64 > max { + Err(TryFromIntError(())) + } else { + Ok(u as $unsigned) + } + } + } + )*} +} + +cross_sign_from_int_impl!(u8, i8, i16, i32, i64, isize); +cross_sign_from_int_impl!(u16, i8, i16, i32, i64, isize); +cross_sign_from_int_impl!(u32, i8, i16, i32, i64, isize); +cross_sign_from_int_impl!(u64, i8, i16, i32, i64, isize); +cross_sign_from_int_impl!(usize, i8, i16, i32, i64, isize); + #[doc(hidden)] trait FromStrRadixHelper: PartialOrd + Copy { fn min_value() -> Self; + fn max_value() -> Self; fn from_u32(u: u32) -> Self; fn checked_mul(&self, other: u32) -> Option<Self>; fn checked_sub(&self, other: u32) -> Option<Self>; @@ -2357,6 +2481,7 @@ trait FromStrRadixHelper: PartialOrd + Copy { macro_rules! doit { ($($t:ty)*) => ($(impl FromStrRadixHelper for $t { fn min_value() -> Self { Self::min_value() } + fn max_value() -> Self { Self::max_value() } fn from_u32(u: u32) -> Self { u as Self } fn checked_mul(&self, other: u32) -> Option<Self> { Self::checked_mul(*self, other as Self) diff --git a/libcore/num/uint_macros.rs b/libcore/num/uint_macros.rs index 6479836..2ab2f95 100644 --- a/libcore/num/uint_macros.rs +++ b/libcore/num/uint_macros.rs @@ -10,13 +10,13 @@ #![doc(hidden)] -macro_rules! uint_module { ($T:ty, $bits:expr) => ( +macro_rules! uint_module { ($T:ident, $bits:expr) => ( #[stable(feature = "rust1", since = "1.0.0")] #[allow(missing_docs)] -pub const MIN: $T = 0 as $T; +pub const MIN: $T = $T::min_value(); #[stable(feature = "rust1", since = "1.0.0")] #[allow(missing_docs)] -pub const MAX: $T = !0 as $T; +pub const MAX: $T = $T::max_value(); ) } diff --git a/libcore/num/usize.rs b/libcore/num/usize.rs index 0c7d16a..685c52e 100644 --- a/libcore/num/usize.rs +++ b/libcore/num/usize.rs @@ -14,6 +14,8 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[cfg(target_pointer_width = "16")] +uint_module! { usize, 16 } #[cfg(target_pointer_width = "32")] uint_module! { usize, 32 } #[cfg(target_pointer_width = "64")] diff --git a/libcore/num/wrapping.rs b/libcore/num/wrapping.rs index e28a36a..4857817 100644 --- a/libcore/num/wrapping.rs +++ b/libcore/num/wrapping.rs @@ -275,6 +275,15 @@ macro_rules! wrapping_impl { *self = *self & other; } } + + #[stable(feature = "wrapping_neg", since = "1.10.0")] + impl Neg for Wrapping<$t> { + type Output = Self; + #[inline(always)] + fn neg(self) -> Self { + Wrapping(0) - self + } + } )*) } @@ -283,6 +292,12 @@ wrapping_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 } mod shift_max { #![allow(non_upper_case_globals)] + #[cfg(target_pointer_width = "16")] + mod platform { + pub const usize: u32 = super::u16; + pub const isize: u32 = super::i16; + } + #[cfg(target_pointer_width = "32")] mod platform { pub const usize: u32 = super::u32; |