Browse Source

Merge #30

30: Re-introduce the std feature r=vks a=cuviper

This is a port of @vks's rust-num/num#296, but without the feature-toggled changes to `Float`.  Now `Float` and the newer `Real` are completely dependent on having `std` enabled.  In the future we can consider adding separate more-limited float/real traits that can work without `std`, like the `BaseFloat` that was originally proposed in the former PR.

This is a breaking change with a bump to 0.2, since anyone currently using `default-features = false` will lose functionality.  The actual API is otherwise unchanged, so my plan is to employ the "semver trick" -- publishing a new num-traits-0.1 that re-exports everything from 0.2 (with `std`).  Thus all `num-traits` users should remain compatible even if they mix 0.1 and 0.2.

Closes #16.
bors[bot] 7 years ago
parent
commit
afa81f80e4
14 changed files with 140 additions and 55 deletions
  1. 5 1
      Cargo.toml
  2. 14 1
      README.md
  3. 4 2
      ci/test_full.sh
  4. 4 4
      src/bounds.rs
  5. 9 6
      src/cast.rs
  6. 17 3
      src/float.rs
  7. 2 2
      src/identities.rs
  8. 1 1
      src/int.rs
  9. 39 24
      src/lib.rs
  10. 1 1
      src/ops/checked.rs
  11. 2 2
      src/ops/wrapping.rs
  12. 1 1
      src/pow.rs
  13. 2 0
      src/real.rs
  14. 39 7
      src/sign.rs

+ 5 - 1
Cargo.toml

@@ -8,7 +8,11 @@ categories = [ "algorithms", "science" ]
 license = "MIT/Apache-2.0"
 repository = "https://github.com/rust-num/num-traits"
 name = "num-traits"
-version = "0.1.42"
+version = "0.2.0-pre"
 readme = "README.md"
 
 [dependencies]
+
+[features]
+default = ["std"]
+std = []

+ 14 - 1
README.md

@@ -12,7 +12,7 @@ Add this to your `Cargo.toml`:
 
 ```toml
 [dependencies]
-num-traits = "0.1"
+num-traits = "0.2"
 ```
 
 and this to your crate root:
@@ -21,6 +21,19 @@ and this to your crate root:
 extern crate num_traits;
 ```
 
+## Features
+
+This crate can be used without the standard library (`#![no_std]`) by disabling
+the default `std` feature.  Use this in `Cargo.toml`:
+
+```toml
+[dependencies.num-traits]
+version = "0.2"
+default-features = false
+```
+
+The `Float` and `Real` traits are only available when `std` is enabled.
+
 ## Releases
 
 Release notes are available in [RELEASES.md](RELEASES.md).

+ 4 - 2
ci/test_full.sh

@@ -4,8 +4,10 @@ set -ex
 
 echo Testing num-traits on rustc ${TRAVIS_RUST_VERSION}
 
-# num-integer should build and test everywhere.
+# num-traits should build and test everywhere.
 cargo build --verbose
 cargo test --verbose
 
-# We have no features to test...
+# test `no_std`
+cargo build --verbose --no-default-features
+cargo test --verbose --no-default-features

+ 4 - 4
src/bounds.rs

@@ -1,7 +1,7 @@
-use std::{usize, u8, u16, u32, u64};
-use std::{isize, i8, i16, i32, i64};
-use std::{f32, f64};
-use std::num::Wrapping;
+use core::{usize, u8, u16, u32, u64};
+use core::{isize, i8, i16, i32, i64};
+use core::{f32, f64};
+use core::num::Wrapping;
 
 /// Numbers which have upper and lower bounds
 pub trait Bounded {

+ 9 - 6
src/cast.rs

@@ -1,5 +1,6 @@
-use std::mem::size_of;
-use std::num::Wrapping;
+use core::f64;
+use core::mem::size_of;
+use core::num::Wrapping;
 
 use identities::Zero;
 use bounds::Bounded;
@@ -226,8 +227,10 @@ macro_rules! impl_to_primitive_float_to_float {
             // Make sure the value is in range for the cast.
             // NaN and +-inf are cast as they are.
             let n = $slf as f64;
-            let max_value: $DstT = ::std::$DstT::MAX;
-            if !n.is_finite() || (-max_value as f64 <= n && n <= max_value as f64) {
+            let max_value: $DstT = ::core::$DstT::MAX;
+            if n != n || n == f64::INFINITY || n == f64::NEG_INFINITY
+                || (-max_value as f64 <= n && n <= max_value as f64)
+            {
                 Some($slf as $DstT)
             } else {
                 None
@@ -522,8 +525,8 @@ impl_as_primitive!(bool => u8, i8, u16, i16, u32, i32, u64, isize, usize, i64);
 
 #[test]
 fn to_primitive_float() {
-    use std::f32;
-    use std::f64;
+    use core::f32;
+    use core::f64;
 
     let f32_toolarge = 1e39f64;
     assert_eq!(f32_toolarge.to_f32(), None);

+ 17 - 3
src/float.rs

@@ -1,15 +1,24 @@
+#[cfg(feature = "std")]
 use std::mem;
+#[cfg(feature = "std")]
 use std::ops::Neg;
+#[cfg(feature = "std")]
 use std::num::FpCategory;
 
 // Used for default implementation of `epsilon`
+#[cfg(feature = "std")]
 use std::f32;
 
+#[cfg(feature = "std")]
 use {Num, NumCast};
 
 // FIXME: these doctests aren't actually helpful, because they're using and
 // testing the inherent methods directly, not going through `Float`.
 
+/// Generic trait for floating point numbers
+///
+/// This trait is only available with the `std` feature.
+#[cfg(feature = "std")]
 pub trait Float
     : Num
     + Copy
@@ -923,6 +932,7 @@ pub trait Float
     fn integer_decode(self) -> (u64, i16, i8);
 }
 
+#[cfg(feature = "std")]
 macro_rules! float_impl {
     ($T:ident $decode:ident) => (
         impl Float for $T {
@@ -1219,6 +1229,7 @@ macro_rules! float_impl {
     )
 }
 
+#[cfg(feature = "std")]
 fn integer_decode_f32(f: f32) -> (u64, i16, i8) {
     let bits: u32 = unsafe { mem::transmute(f) };
     let sign: i8 = if bits >> 31 == 0 {
@@ -1237,6 +1248,7 @@ fn integer_decode_f32(f: f32) -> (u64, i16, i8) {
     (mantissa as u64, exponent, sign)
 }
 
+#[cfg(feature = "std")]
 fn integer_decode_f64(f: f64) -> (u64, i16, i8) {
     let bits: u64 = unsafe { mem::transmute(f) };
     let sign: i8 = if bits >> 63 == 0 {
@@ -1255,7 +1267,9 @@ fn integer_decode_f64(f: f64) -> (u64, i16, i8) {
     (mantissa, exponent, sign)
 }
 
+#[cfg(feature = "std")]
 float_impl!(f32 integer_decode_f32);
+#[cfg(feature = "std")]
 float_impl!(f64 integer_decode_f64);
 
 macro_rules! float_const_impl {
@@ -1272,7 +1286,7 @@ macro_rules! float_const_impl {
             $(
                 #[inline]
                 fn $constant() -> Self {
-                    ::std::$T::consts::$constant
+                    ::core::$T::consts::$constant
                 }
             )+
         }
@@ -1314,13 +1328,13 @@ float_const_impl! {
     SQRT_2,
 }
 
-#[cfg(test)]
+#[cfg(all(test, feature = "std"))]
 mod tests {
     use Float;
 
     #[test]
     fn convert_deg_rad() {
-        use std::f64::consts;
+        use core::f64::consts;
 
         const DEG_RAD_PAIRS: [(f64, f64); 7] = [
             (0.0, 0.),

+ 2 - 2
src/identities.rs

@@ -1,5 +1,5 @@
-use std::ops::{Add, Mul};
-use std::num::Wrapping;
+use core::ops::{Add, Mul};
+use core::num::Wrapping;
 
 /// Defines an additive identity element for `Self`.
 pub trait Zero: Sized + Add<Self, Output = Self> {

+ 1 - 1
src/int.rs

@@ -1,4 +1,4 @@
-use std::ops::{Not, BitAnd, BitOr, BitXor, Shl, Shr};
+use core::ops::{Not, BitAnd, BitOr, BitXor, Shl, Shr};
 
 use {Num, NumCast};
 use bounds::Bounded;

+ 39 - 24
src/lib.rs

@@ -10,22 +10,30 @@
 
 //! Numeric traits for generic mathematics
 
-#![doc(html_root_url = "https://docs.rs/num-traits/0.1")]
+#![doc(html_root_url = "https://docs.rs/num-traits/0.2")]
 
-use std::ops::{Add, Sub, Mul, Div, Rem};
-use std::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign};
-use std::num::Wrapping;
-use std::fmt;
+#![deny(unconditional_recursion)]
+
+#![cfg_attr(not(feature = "std"), no_std)]
+#[cfg(feature = "std")]
+extern crate core;
+
+use core::ops::{Add, Sub, Mul, Div, Rem};
+use core::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign};
+use core::num::Wrapping;
+use core::fmt;
 
 pub use bounds::Bounded;
-pub use float::{Float, FloatConst};
+#[cfg(feature = "std")]
+pub use float::Float;
+pub use float::FloatConst;
 // pub use real::Real; // NOTE: Don't do this, it breaks `use num_traits::*;`.
 pub use identities::{Zero, One, zero, one};
-pub use ops::checked::*;
-pub use ops::wrapping::*;
+pub use ops::checked::{CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedShl, CheckedShr};
+pub use ops::wrapping::{WrappingAdd, WrappingMul, WrappingSub};
 pub use ops::saturating::Saturating;
 pub use sign::{Signed, Unsigned, abs, abs_sub, signum};
-pub use cast::*;
+pub use cast::{AsPrimitive, FromPrimitive, ToPrimitive, NumCast, cast};
 pub use int::PrimInt;
 pub use pow::{pow, checked_pow};
 
@@ -34,6 +42,7 @@ pub mod sign;
 pub mod ops;
 pub mod bounds;
 pub mod float;
+#[cfg(feature = "std")]
 pub mod real;
 pub mod cast;
 pub mod int;
@@ -130,10 +139,10 @@ impl<T> NumAssignRef for T where T: NumAssign + for<'r> NumAssignOps<&'r T> {}
 macro_rules! int_trait_impl {
     ($name:ident for $($t:ty)*) => ($(
         impl $name for $t {
-            type FromStrRadixErr = ::std::num::ParseIntError;
+            type FromStrRadixErr = ::core::num::ParseIntError;
             #[inline]
             fn from_str_radix(s: &str, radix: u32)
-                              -> Result<Self, ::std::num::ParseIntError>
+                              -> Result<Self, ::core::num::ParseIntError>
             {
                 <$t>::from_str_radix(s, radix)
             }
@@ -159,7 +168,7 @@ pub enum FloatErrorKind {
     Empty,
     Invalid,
 }
-// FIXME: std::num::ParseFloatError is stable in 1.0, but opaque to us,
+// FIXME: core::num::ParseFloatError is stable in 1.0, but opaque to us,
 // so there's not really any way for us to reuse it.
 #[derive(Debug)]
 pub struct ParseFloatError {
@@ -181,7 +190,7 @@ impl fmt::Display for ParseFloatError {
 // with this implementation ourselves until we want to make a breaking change.
 // (would have to drop it from `Num` though)
 macro_rules! float_trait_impl {
-    ($name:ident for $($t:ty)*) => ($(
+    ($name:ident for $($t:ident)*) => ($(
         impl $name for $t {
             type FromStrRadixErr = ParseFloatError;
 
@@ -193,9 +202,9 @@ macro_rules! float_trait_impl {
 
                 // Special values
                 match src {
-                    "inf"   => return Ok(Float::infinity()),
-                    "-inf"  => return Ok(Float::neg_infinity()),
-                    "NaN"   => return Ok(Float::nan()),
+                    "inf"   => return Ok(core::$t::INFINITY),
+                    "-inf"  => return Ok(core::$t::NEG_INFINITY),
+                    "NaN"   => return Ok(core::$t::NAN),
                     _       => {},
                 }
 
@@ -236,15 +245,15 @@ macro_rules! float_trait_impl {
                             // if we've not seen any non-zero digits.
                             if prev_sig != 0.0 {
                                 if is_positive && sig <= prev_sig
-                                    { return Ok(Float::infinity()); }
+                                    { return Ok(core::$t::INFINITY); }
                                 if !is_positive && sig >= prev_sig
-                                    { return Ok(Float::neg_infinity()); }
+                                    { return Ok(core::$t::NEG_INFINITY); }
 
                                 // Detect overflow by reversing the shift-and-add process
                                 if is_positive && (prev_sig != (sig - digit as $t) / radix as $t)
-                                    { return Ok(Float::infinity()); }
+                                    { return Ok(core::$t::INFINITY); }
                                 if !is_positive && (prev_sig != (sig + digit as $t) / radix as $t)
-                                    { return Ok(Float::neg_infinity()); }
+                                    { return Ok(core::$t::NEG_INFINITY); }
                             }
                             prev_sig = sig;
                         },
@@ -280,9 +289,9 @@ macro_rules! float_trait_impl {
                                 };
                                 // Detect overflow by comparing to last value
                                 if is_positive && sig < prev_sig
-                                    { return Ok(Float::infinity()); }
+                                    { return Ok(core::$t::INFINITY); }
                                 if !is_positive && sig > prev_sig
-                                    { return Ok(Float::neg_infinity()); }
+                                    { return Ok(core::$t::NEG_INFINITY); }
                                 prev_sig = sig;
                             },
                             None => match c {
@@ -316,9 +325,15 @@ macro_rules! float_trait_impl {
                             None             => return Err(PFE { kind: Invalid }),
                         };
 
+                        #[cfg(feature = "std")]
+                        fn pow(base: $t, exp: usize) -> $t {
+                            Float::powi(base, exp as i32)
+                        }
+                        // otherwise uses the generic `pow` from the root
+
                         match (is_positive, exp) {
-                            (true,  Ok(exp)) => base.powi(exp as i32),
-                            (false, Ok(exp)) => 1.0 / base.powi(exp as i32),
+                            (true,  Ok(exp)) => pow(base, exp),
+                            (false, Ok(exp)) => 1.0 / pow(base, exp),
                             (_, Err(_))      => return Err(PFE { kind: Invalid }),
                         }
                     },

+ 1 - 1
src/ops/checked.rs

@@ -1,4 +1,4 @@
-use std::ops::{Add, Sub, Mul, Div, Shl, Shr};
+use core::ops::{Add, Sub, Mul, Div, Shl, Shr};
 
 /// Performs addition that returns `None` instead of wrapping around on
 /// overflow.

+ 2 - 2
src/ops/wrapping.rs

@@ -1,5 +1,5 @@
-use std::ops::{Add, Sub, Mul};
-use std::num::Wrapping;
+use core::ops::{Add, Sub, Mul};
+use core::num::Wrapping;
 
 macro_rules! wrapping_impl {
     ($trait_name:ident, $method:ident, $t:ty) => {

+ 1 - 1
src/pow.rs

@@ -1,4 +1,4 @@
-use std::ops::Mul;
+use core::ops::Mul;
 use {One, CheckedMul};
 
 /// Raises a value to the power of exp, using exponentiation by squaring.

+ 2 - 0
src/real.rs

@@ -10,6 +10,8 @@ use {Num, NumCast, Float};
 ///
 /// See [this Wikipedia article](https://en.wikipedia.org/wiki/Real_data_type)
 /// for a list of data types that could meaningfully implement this trait.
+///
+/// This trait is only available with the `std` feature.
 pub trait Real
     : Num
     + Copy

+ 39 - 7
src/sign.rs

@@ -1,6 +1,6 @@
-use std::ops::Neg;
-use std::{f32, f64};
-use std::num::Wrapping;
+use core::ops::Neg;
+use core::{f32, f64};
+use core::num::Wrapping;
 
 use Num;
 
@@ -103,17 +103,42 @@ macro_rules! signed_float_impl {
         impl Signed for $t {
             /// Computes the absolute value. Returns `NAN` if the number is `NAN`.
             #[inline]
+            #[cfg(feature = "std")]
             fn abs(&self) -> $t {
-                <$t>::abs(*self)
+                (*self).abs()
+            }
+
+            /// Computes the absolute value. Returns `NAN` if the number is `NAN`.
+            #[inline]
+            #[cfg(not(feature = "std"))]
+            fn abs(&self) -> $t {
+                if self.is_positive() {
+                    *self
+                } else if self.is_negative() {
+                    -*self
+                } else {
+                    $nan
+                }
             }
 
             /// The positive difference of two numbers. Returns `0.0` if the number is
             /// less than or equal to `other`, otherwise the difference between`self`
             /// and `other` is returned.
             #[inline]
-            #[allow(deprecated)]
             fn abs_sub(&self, other: &$t) -> $t {
-                <$t>::abs_sub(*self, *other)
+                if *self <= *other { 0. } else { *self - *other }
+            }
+
+            /// # Returns
+            ///
+            /// - `1.0` if the number is positive, `+0.0` or `INFINITY`
+            /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
+            /// - `NAN` if the number is NaN
+            #[inline]
+            #[cfg(feature = "std")]
+            fn signum(&self) -> $t {
+                use Float;
+                Float::signum(*self)
             }
 
             /// # Returns
@@ -122,8 +147,15 @@ macro_rules! signed_float_impl {
             /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
             /// - `NAN` if the number is NaN
             #[inline]
+            #[cfg(not(feature = "std"))]
             fn signum(&self) -> $t {
-                <$t>::signum(*self)
+                if self.is_positive() {
+                    1.0
+                } else if self.is_negative() {
+                    -1.0
+                } else {
+                    $nan
+                }
             }
 
             /// Returns `true` if the number is positive, including `+0.0` and `INFINITY`