traits: Introduce std feature

This makes it possible to build `traits` without `std`. For this a new
trait `BasicFloat` was introduced, implementing some basic functionality
that works with `core`. Most notably this is lacking functions like
`cos`, `sin`, etc.

`Float` is not available without `std`.

Refs #216.
This commit is contained in:
Vinzent Steinberg 2017-05-30 16:42:26 +02:00
parent ef752e4687
commit 351dfc6383
11 changed files with 286 additions and 68 deletions

View File

@ -10,3 +10,7 @@ name = "num-traits"
version = "0.1.37" version = "0.1.37"
[dependencies] [dependencies]
[features]
default = ["std"]
std = []

View File

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

View File

@ -1,8 +1,9 @@
use std::mem::size_of; use core::mem::size_of;
use std::num::Wrapping; use core::num::Wrapping;
use identities::Zero; use identities::Zero;
use bounds::Bounded; use bounds::Bounded;
use float::BasicFloat;
/// A generic trait for converting a value to a number. /// A generic trait for converting a value to a number.
pub trait ToPrimitive { pub trait ToPrimitive {
@ -226,8 +227,8 @@ macro_rules! impl_to_primitive_float_to_float {
// Make sure the value is in range for the cast. // Make sure the value is in range for the cast.
// NaN and +-inf are cast as they are. // NaN and +-inf are cast as they are.
let n = $slf as f64; let n = $slf as f64;
let max_value: $DstT = ::std::$DstT::MAX; let max_value: $DstT = ::core::$DstT::MAX;
if !n.is_finite() || (-max_value as f64 <= n && n <= max_value as f64) { if !BasicFloat::is_finite(n) || (-max_value as f64 <= n && n <= max_value as f64) {
Some($slf as $DstT) Some($slf as $DstT)
} else { } else {
None None
@ -454,8 +455,8 @@ impl<T: NumCast> NumCast for Wrapping<T> {
#[test] #[test]
fn to_primitive_float() { fn to_primitive_float() {
use std::f32; use core::f32;
use std::f64; use core::f64;
let f32_toolarge = 1e39f64; let f32_toolarge = 1e39f64;
assert_eq!(f32_toolarge.to_f32(), None); assert_eq!(f32_toolarge.to_f32(), None);

View File

@ -1,15 +1,209 @@
use std::mem; use core::mem;
use std::ops::Neg; use core::ops::Neg;
use std::num::FpCategory; use core::num::FpCategory;
// Used for default implementation of `epsilon` // Used for default implementation of `epsilon`
use std::f32; use core::f32;
use {Num, NumCast}; use {Num, NumCast};
/// Basic floating point operations that work with `core`.
pub trait BasicFloat: Num + Neg<Output = Self> + PartialOrd + Copy {
/// Returns positive infinity.
#[inline]
fn infinity() -> Self;
/// Returns negative infinity.
#[inline]
fn neg_infinity() -> Self;
/// Returns NaN.
#[inline]
fn nan() -> Self;
/// Returns `true` if the number is NaN.
#[inline]
fn is_nan(self) -> bool {
self != self
}
/// Returns `true` if the number is infinite.
#[inline]
fn is_infinite(self) -> bool {
self == Self::infinity() || self == Self::neg_infinity()
}
/// Returns `true` if the number is neither infinite or NaN.
#[inline]
fn is_finite(self) -> bool {
!(self.is_nan() || self.is_infinite())
}
/// Returns `true` if the number is neither zero, infinite, subnormal or NaN.
#[inline]
fn is_normal(self) -> bool {
self.classify() == FpCategory::Normal
}
/// Returns the floating point category of the number. If only one property
/// is going to be tested, it is generally faster to use the specific
/// predicate instead.
#[inline]
fn classify(self) -> FpCategory;
/// Computes the absolute value of `self`. Returns `BasicFloat::nan()` if the
/// number is `BasicFloat::nan()`.
#[inline]
fn abs(self) -> Self {
if self.is_sign_positive() {
return self;
}
if self.is_sign_negative() {
return -self;
}
Self::nan()
}
/// Returns a number that represents the sign of `self`.
///
/// - `1.0` if the number is positive, `+0.0` or `BasicFloat::infinity()`
/// - `-1.0` if the number is negative, `-0.0` or `BasicFloat::neg_infinity()`
/// - `BasicFloat::nan()` if the number is `BasicFloat::nan()`
#[inline]
fn signum(self) -> Self {
if self.is_sign_positive() {
return Self::one();
}
if self.is_sign_negative() {
return -Self::one();
}
Self::nan()
}
/// Returns `true` if `self` is positive, including `+0.0` and
/// `BasicFloat::infinity()`.
#[inline]
fn is_sign_positive(self) -> bool {
self > Self::zero() || (Self::one() / self) == Self::infinity()
}
/// Returns `true` if `self` is negative, including `-0.0` and
/// `BasicFloat::neg_infinity()`.
#[inline]
fn is_sign_negative(self) -> bool {
self < Self::zero() || (Self::one() / self) == Self::neg_infinity()
}
/// Returns the reciprocal (multiplicative inverse) of the number.
#[inline]
fn recip(self) -> Self {
Self::one() / self
}
#[inline]
fn powi(self, mut exp: i32) -> Self {
if exp == 0 { return Self::one() }
let mut base;
if exp < 0 {
exp = -exp;
base = self.recip();
} else {
base = self;
}
while exp & 1 == 0 {
base = base * base;
exp >>= 1;
}
if exp == 1 { return base }
let mut acc = base;
while exp > 1 {
exp >>= 1;
base = base * base;
if exp & 1 == 1 {
acc = acc * base;
}
}
acc
}
/// Converts to degrees, assuming the number is in radians.
#[inline]
fn to_degrees(self) -> Self;
/// Converts to radians, assuming the number is in degrees.
#[inline]
fn to_radians(self) -> Self;
}
impl BasicFloat for f32 {
fn infinity() -> Self {
::core::f32::INFINITY
}
fn neg_infinity() -> Self {
::core::f32::INFINITY
}
fn nan() -> Self {
::core::f32::NAN
}
fn classify(self) -> FpCategory {
const EXP_MASK: u32 = 0x7f800000;
const MAN_MASK: u32 = 0x007fffff;
let bits: u32 = unsafe { mem::transmute(self) };
match (bits & MAN_MASK, bits & EXP_MASK) {
(0, 0) => FpCategory::Zero,
(_, 0) => FpCategory::Subnormal,
(0, EXP_MASK) => FpCategory::Infinite,
(_, EXP_MASK) => FpCategory::Nan,
_ => FpCategory::Normal,
}
}
fn to_degrees(self) -> Self {
self * (180.0 / ::core::f32::consts::PI)
}
fn to_radians(self) -> Self {
self * (::core::f32::consts::PI / 180.0)
}
}
impl BasicFloat for f64 {
fn infinity() -> Self {
::core::f64::INFINITY
}
fn neg_infinity() -> Self {
::core::f64::INFINITY
}
fn nan() -> Self {
::core::f64::NAN
}
fn classify(self) -> FpCategory {
const EXP_MASK: u64 = 0x7ff0000000000000;
const MAN_MASK: u64 = 0x000fffffffffffff;
let bits: u64 = unsafe { mem::transmute(self) };
match (bits & MAN_MASK, bits & EXP_MASK) {
(0, 0) => FpCategory::Zero,
(_, 0) => FpCategory::Subnormal,
(0, EXP_MASK) => FpCategory::Infinite,
(_, EXP_MASK) => FpCategory::Nan,
_ => FpCategory::Normal,
}
}
fn to_degrees(self) -> Self {
self * (180.0 / ::core::f64::consts::PI)
}
fn to_radians(self) -> Self {
self * (::core::f64::consts::PI / 180.0)
}
}
// FIXME: these doctests aren't actually helpful, because they're using and // FIXME: these doctests aren't actually helpful, because they're using and
// testing the inherent methods directly, not going through `Float`. // testing the inherent methods directly, not going through `Float`.
/// Floating point operations that work with `std`.
pub trait Float pub trait Float
: Num : Num
+ Copy + Copy
@ -930,17 +1124,17 @@ macro_rules! float_impl {
impl Float for $T { impl Float for $T {
#[inline] #[inline]
fn nan() -> Self { fn nan() -> Self {
::std::$T::NAN ::core::$T::NAN
} }
#[inline] #[inline]
fn infinity() -> Self { fn infinity() -> Self {
::std::$T::INFINITY ::core::$T::INFINITY
} }
#[inline] #[inline]
fn neg_infinity() -> Self { fn neg_infinity() -> Self {
::std::$T::NEG_INFINITY ::core::$T::NEG_INFINITY
} }
#[inline] #[inline]
@ -950,22 +1144,22 @@ macro_rules! float_impl {
#[inline] #[inline]
fn min_value() -> Self { fn min_value() -> Self {
::std::$T::MIN ::core::$T::MIN
} }
#[inline] #[inline]
fn min_positive_value() -> Self { fn min_positive_value() -> Self {
::std::$T::MIN_POSITIVE ::core::$T::MIN_POSITIVE
} }
#[inline] #[inline]
fn epsilon() -> Self { fn epsilon() -> Self {
::std::$T::EPSILON ::core::$T::EPSILON
} }
#[inline] #[inline]
fn max_value() -> Self { fn max_value() -> Self {
::std::$T::MAX ::core::$T::MAX
} }
#[inline] #[inline]
@ -1097,14 +1291,14 @@ macro_rules! float_impl {
fn to_degrees(self) -> Self { fn to_degrees(self) -> Self {
// NB: `f32` didn't stabilize this until 1.7 // NB: `f32` didn't stabilize this until 1.7
// <$T>::to_degrees(self) // <$T>::to_degrees(self)
self * (180. / ::std::$T::consts::PI) self * (180. / ::core::$T::consts::PI)
} }
#[inline] #[inline]
fn to_radians(self) -> Self { fn to_radians(self) -> Self {
// NB: `f32` didn't stabilize this until 1.7 // NB: `f32` didn't stabilize this until 1.7
// <$T>::to_radians(self) // <$T>::to_radians(self)
self * (::std::$T::consts::PI / 180.) self * (::core::$T::consts::PI / 180.)
} }
#[inline] #[inline]
@ -1257,7 +1451,9 @@ fn integer_decode_f64(f: f64) -> (u64, i16, i8) {
(mantissa, exponent, sign) (mantissa, exponent, sign)
} }
#[cfg(feature = "std")]
float_impl!(f32 integer_decode_f32); float_impl!(f32 integer_decode_f32);
#[cfg(feature = "std")]
float_impl!(f64 integer_decode_f64); float_impl!(f64 integer_decode_f64);
macro_rules! float_const_impl { macro_rules! float_const_impl {
@ -1274,7 +1470,7 @@ macro_rules! float_const_impl {
$( $(
#[inline] #[inline]
fn $constant() -> Self { fn $constant() -> Self {
::std::$T::consts::$constant ::core::$T::consts::$constant
} }
)+ )+
} }
@ -1318,11 +1514,8 @@ float_const_impl! {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use Float; use BasicFloat;
use core::f64::consts;
#[test]
fn convert_deg_rad() {
use std::f64::consts;
const DEG_RAD_PAIRS: [(f64, f64); 7] = [ const DEG_RAD_PAIRS: [(f64, f64); 7] = [
(0.0, 0.), (0.0, 0.),
@ -1334,6 +1527,23 @@ mod tests {
(180.0, consts::PI), (180.0, consts::PI),
]; ];
#[test]
fn convert_deg_rad_core() {
for &(deg, rad) in &DEG_RAD_PAIRS {
assert!((BasicFloat::to_degrees(rad) - deg).abs() < 1e-6);
assert!((BasicFloat::to_radians(deg) - rad).abs() < 1e-6);
let (deg, rad) = (deg as f32, rad as f32);
assert!((BasicFloat::to_degrees(rad) - deg).abs() < 1e-6);
assert!((BasicFloat::to_radians(deg) - rad).abs() < 1e-6);
}
}
#[cfg(std)]
#[test]
fn convert_deg_rad_std() {
use Float;
for &(deg, rad) in &DEG_RAD_PAIRS { for &(deg, rad) in &DEG_RAD_PAIRS {
assert!((Float::to_degrees(rad) - deg).abs() < 1e-6); assert!((Float::to_degrees(rad) - deg).abs() < 1e-6);
assert!((Float::to_radians(deg) - rad).abs() < 1e-6); assert!((Float::to_radians(deg) - rad).abs() < 1e-6);

View File

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

View File

@ -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 {Num, NumCast};
use bounds::Bounded; use bounds::Bounded;

View File

@ -14,12 +14,16 @@
html_root_url = "https://rust-num.github.io/num/", html_root_url = "https://rust-num.github.io/num/",
html_playground_url = "http://play.integer32.com/")] html_playground_url = "http://play.integer32.com/")]
use std::ops::{Add, Sub, Mul, Div, Rem}; #![cfg_attr(not(feature = "std"), no_std)]
use std::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign}; #[cfg(feature = "std")]
use std::num::Wrapping; extern crate core;
use core::ops::{Add, Sub, Mul, Div, Rem};
use core::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign};
use core::num::Wrapping;
pub use bounds::Bounded; pub use bounds::Bounded;
pub use float::{Float, FloatConst}; pub use float::{BasicFloat, Float, FloatConst};
pub use identities::{Zero, One, zero, one}; pub use identities::{Zero, One, zero, one};
pub use ops::checked::*; pub use ops::checked::*;
pub use ops::wrapping::*; pub use ops::wrapping::*;
@ -129,10 +133,10 @@ impl<T> NumAssignRef for T where T: NumAssign + for<'r> NumAssignOps<&'r T> {}
macro_rules! int_trait_impl { macro_rules! int_trait_impl {
($name:ident for $($t:ty)*) => ($( ($name:ident for $($t:ty)*) => ($(
impl $name for $t { impl $name for $t {
type FromStrRadixErr = ::std::num::ParseIntError; type FromStrRadixErr = ::core::num::ParseIntError;
#[inline] #[inline]
fn from_str_radix(s: &str, radix: u32) fn from_str_radix(s: &str, radix: u32)
-> Result<Self, ::std::num::ParseIntError> -> Result<Self, ::core::num::ParseIntError>
{ {
<$t>::from_str_radix(s, radix) <$t>::from_str_radix(s, radix)
} }
@ -158,7 +162,7 @@ pub enum FloatErrorKind {
Empty, Empty,
Invalid, 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. // so there's not really any way for us to reuse it.
#[derive(Debug)] #[derive(Debug)]
pub struct ParseFloatError { pub struct ParseFloatError {
@ -181,9 +185,9 @@ macro_rules! float_trait_impl {
// Special values // Special values
match src { match src {
"inf" => return Ok(Float::infinity()), "inf" => return Ok(BasicFloat::infinity()),
"-inf" => return Ok(Float::neg_infinity()), "-inf" => return Ok(BasicFloat::neg_infinity()),
"NaN" => return Ok(Float::nan()), "NaN" => return Ok(BasicFloat::nan()),
_ => {}, _ => {},
} }
@ -224,15 +228,15 @@ macro_rules! float_trait_impl {
// if we've not seen any non-zero digits. // if we've not seen any non-zero digits.
if prev_sig != 0.0 { if prev_sig != 0.0 {
if is_positive && sig <= prev_sig if is_positive && sig <= prev_sig
{ return Ok(Float::infinity()); } { return Ok(BasicFloat::infinity()); }
if !is_positive && sig >= prev_sig if !is_positive && sig >= prev_sig
{ return Ok(Float::neg_infinity()); } { return Ok(BasicFloat::neg_infinity()); }
// Detect overflow by reversing the shift-and-add process // Detect overflow by reversing the shift-and-add process
if is_positive && (prev_sig != (sig - digit as $t) / radix as $t) if is_positive && (prev_sig != (sig - digit as $t) / radix as $t)
{ return Ok(Float::infinity()); } { return Ok(BasicFloat::infinity()); }
if !is_positive && (prev_sig != (sig + digit as $t) / radix as $t) if !is_positive && (prev_sig != (sig + digit as $t) / radix as $t)
{ return Ok(Float::neg_infinity()); } { return Ok(BasicFloat::neg_infinity()); }
} }
prev_sig = sig; prev_sig = sig;
}, },
@ -268,9 +272,9 @@ macro_rules! float_trait_impl {
}; };
// Detect overflow by comparing to last value // Detect overflow by comparing to last value
if is_positive && sig < prev_sig if is_positive && sig < prev_sig
{ return Ok(Float::infinity()); } { return Ok(BasicFloat::infinity()); }
if !is_positive && sig > prev_sig if !is_positive && sig > prev_sig
{ return Ok(Float::neg_infinity()); } { return Ok(BasicFloat::neg_infinity()); }
prev_sig = sig; prev_sig = sig;
}, },
None => match c { None => match c {
@ -305,8 +309,8 @@ macro_rules! float_trait_impl {
}; };
match (is_positive, exp) { match (is_positive, exp) {
(true, Ok(exp)) => base.powi(exp as i32), (true, Ok(exp)) => BasicFloat::powi(base, exp as i32),
(false, Ok(exp)) => 1.0 / base.powi(exp as i32), (false, Ok(exp)) => 1.0 / BasicFloat::powi(base, exp as i32),
(_, Err(_)) => return Err(PFE { kind: Invalid }), (_, Err(_)) => return Err(PFE { kind: Invalid }),
} }
}, },

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
use std::ops::Neg; use core::ops::Neg;
use std::{f32, f64}; use core::{f32, f64};
use std::num::Wrapping; use core::num::Wrapping;
use Num; use Num;
@ -104,16 +104,15 @@ macro_rules! signed_float_impl {
/// Computes the absolute value. Returns `NAN` if the number is `NAN`. /// Computes the absolute value. Returns `NAN` if the number is `NAN`.
#[inline] #[inline]
fn abs(&self) -> $t { fn abs(&self) -> $t {
<$t>::abs(*self) (*self).abs()
} }
/// The positive difference of two numbers. Returns `0.0` if the number is /// The positive difference of two numbers. Returns `0.0` if the number is
/// less than or equal to `other`, otherwise the difference between`self` /// less than or equal to `other`, otherwise the difference between`self`
/// and `other` is returned. /// and `other` is returned.
#[inline] #[inline]
#[allow(deprecated)]
fn abs_sub(&self, other: &$t) -> $t { fn abs_sub(&self, other: &$t) -> $t {
<$t>::abs_sub(*self, *other) if *self <= *other { 0. } else { *self - *other }
} }
/// # Returns /// # Returns
@ -123,7 +122,7 @@ macro_rules! signed_float_impl {
/// - `NAN` if the number is NaN /// - `NAN` if the number is NaN
#[inline] #[inline]
fn signum(&self) -> $t { fn signum(&self) -> $t {
<$t>::signum(*self) (*self).signum()
} }
/// Returns `true` if the number is positive, including `+0.0` and `INFINITY` /// Returns `true` if the number is positive, including `+0.0` and `INFINITY`