aboutsummaryrefslogtreecommitdiff
path: root/libcore/num
diff options
context:
space:
mode:
Diffstat (limited to 'libcore/num')
-rw-r--r--libcore/num/bignum.rs2
-rw-r--r--libcore/num/dec2flt/algorithm.rs89
-rw-r--r--libcore/num/int_macros.rs11
-rw-r--r--libcore/num/isize.rs2
-rw-r--r--libcore/num/mod.rs133
-rw-r--r--libcore/num/uint_macros.rs6
-rw-r--r--libcore/num/usize.rs2
-rw-r--r--libcore/num/wrapping.rs15
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;