From 351dfc638318af55026394913795196730bd3159 Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Tue, 30 May 2017 16:42:26 +0200 Subject: [PATCH] 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. --- traits/Cargo.toml | 4 + traits/src/bounds.rs | 8 +- traits/src/cast.rs | 13 +- traits/src/float.rs | 262 +++++++++++++++++++++++++++++++++---- traits/src/identities.rs | 4 +- traits/src/int.rs | 2 +- traits/src/lib.rs | 40 +++--- traits/src/ops/checked.rs | 2 +- traits/src/ops/wrapping.rs | 4 +- traits/src/pow.rs | 2 +- traits/src/sign.rs | 13 +- 11 files changed, 286 insertions(+), 68 deletions(-) diff --git a/traits/Cargo.toml b/traits/Cargo.toml index e0b996a..933df55 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -10,3 +10,7 @@ name = "num-traits" version = "0.1.37" [dependencies] + +[features] +default = ["std"] +std = [] diff --git a/traits/src/bounds.rs b/traits/src/bounds.rs index ea5c811..95d341c 100644 --- a/traits/src/bounds.rs +++ b/traits/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 { diff --git a/traits/src/cast.rs b/traits/src/cast.rs index 8949834..8923ab4 100644 --- a/traits/src/cast.rs +++ b/traits/src/cast.rs @@ -1,8 +1,9 @@ -use std::mem::size_of; -use std::num::Wrapping; +use core::mem::size_of; +use core::num::Wrapping; use identities::Zero; use bounds::Bounded; +use float::BasicFloat; /// A generic trait for converting a value to a number. 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. // 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 !BasicFloat::is_finite(n) || (-max_value as f64 <= n && n <= max_value as f64) { Some($slf as $DstT) } else { None @@ -454,8 +455,8 @@ impl NumCast for Wrapping { #[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); diff --git a/traits/src/float.rs b/traits/src/float.rs index 757be87..72de9f3 100644 --- a/traits/src/float.rs +++ b/traits/src/float.rs @@ -1,15 +1,209 @@ -use std::mem; -use std::ops::Neg; -use std::num::FpCategory; +use core::mem; +use core::ops::Neg; +use core::num::FpCategory; // Used for default implementation of `epsilon` -use std::f32; +use core::f32; use {Num, NumCast}; +/// Basic floating point operations that work with `core`. +pub trait BasicFloat: Num + Neg + 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 // testing the inherent methods directly, not going through `Float`. +/// Floating point operations that work with `std`. pub trait Float : Num + Copy @@ -930,17 +1124,17 @@ macro_rules! float_impl { impl Float for $T { #[inline] fn nan() -> Self { - ::std::$T::NAN + ::core::$T::NAN } #[inline] fn infinity() -> Self { - ::std::$T::INFINITY + ::core::$T::INFINITY } #[inline] fn neg_infinity() -> Self { - ::std::$T::NEG_INFINITY + ::core::$T::NEG_INFINITY } #[inline] @@ -950,22 +1144,22 @@ macro_rules! float_impl { #[inline] fn min_value() -> Self { - ::std::$T::MIN + ::core::$T::MIN } #[inline] fn min_positive_value() -> Self { - ::std::$T::MIN_POSITIVE + ::core::$T::MIN_POSITIVE } #[inline] fn epsilon() -> Self { - ::std::$T::EPSILON + ::core::$T::EPSILON } #[inline] fn max_value() -> Self { - ::std::$T::MAX + ::core::$T::MAX } #[inline] @@ -1097,14 +1291,14 @@ macro_rules! float_impl { fn to_degrees(self) -> Self { // NB: `f32` didn't stabilize this until 1.7 // <$T>::to_degrees(self) - self * (180. / ::std::$T::consts::PI) + self * (180. / ::core::$T::consts::PI) } #[inline] fn to_radians(self) -> Self { // NB: `f32` didn't stabilize this until 1.7 // <$T>::to_radians(self) - self * (::std::$T::consts::PI / 180.) + self * (::core::$T::consts::PI / 180.) } #[inline] @@ -1257,7 +1451,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 { @@ -1274,7 +1470,7 @@ macro_rules! float_const_impl { $( #[inline] fn $constant() -> Self { - ::std::$T::consts::$constant + ::core::$T::consts::$constant } )+ } @@ -1318,21 +1514,35 @@ float_const_impl! { #[cfg(test)] mod tests { - use Float; + use BasicFloat; + use core::f64::consts; + + const DEG_RAD_PAIRS: [(f64, f64); 7] = [ + (0.0, 0.), + (22.5, consts::FRAC_PI_8), + (30.0, consts::FRAC_PI_6), + (45.0, consts::FRAC_PI_4), + (60.0, consts::FRAC_PI_3), + (90.0, consts::FRAC_PI_2), + (180.0, consts::PI), + ]; #[test] - fn convert_deg_rad() { - use std::f64::consts; + 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); - const DEG_RAD_PAIRS: [(f64, f64); 7] = [ - (0.0, 0.), - (22.5, consts::FRAC_PI_8), - (30.0, consts::FRAC_PI_6), - (45.0, consts::FRAC_PI_4), - (60.0, consts::FRAC_PI_3), - (90.0, consts::FRAC_PI_2), - (180.0, consts::PI), - ]; + 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 { assert!((Float::to_degrees(rad) - deg).abs() < 1e-6); diff --git a/traits/src/identities.rs b/traits/src/identities.rs index 2f520e0..3b32682 100644 --- a/traits/src/identities.rs +++ b/traits/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 { diff --git a/traits/src/int.rs b/traits/src/int.rs index 4f9221f..84fedc6 100644 --- a/traits/src/int.rs +++ b/traits/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; diff --git a/traits/src/lib.rs b/traits/src/lib.rs index 41422c8..67370f4 100644 --- a/traits/src/lib.rs +++ b/traits/src/lib.rs @@ -14,12 +14,16 @@ html_root_url = "https://rust-num.github.io/num/", html_playground_url = "http://play.integer32.com/")] -use std::ops::{Add, Sub, Mul, Div, Rem}; -use std::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign}; -use std::num::Wrapping; +#![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; pub use bounds::Bounded; -pub use float::{Float, FloatConst}; +pub use float::{BasicFloat, Float, FloatConst}; pub use identities::{Zero, One, zero, one}; pub use ops::checked::*; pub use ops::wrapping::*; @@ -129,10 +133,10 @@ impl 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 + -> Result { <$t>::from_str_radix(s, radix) } @@ -158,7 +162,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,9 +185,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(BasicFloat::infinity()), + "-inf" => return Ok(BasicFloat::neg_infinity()), + "NaN" => return Ok(BasicFloat::nan()), _ => {}, } @@ -224,15 +228,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(BasicFloat::infinity()); } 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 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) - { return Ok(Float::neg_infinity()); } + { return Ok(BasicFloat::neg_infinity()); } } prev_sig = sig; }, @@ -268,9 +272,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(BasicFloat::infinity()); } if !is_positive && sig > prev_sig - { return Ok(Float::neg_infinity()); } + { return Ok(BasicFloat::neg_infinity()); } prev_sig = sig; }, None => match c { @@ -305,8 +309,8 @@ macro_rules! float_trait_impl { }; 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)) => BasicFloat::powi(base, exp as i32), + (false, Ok(exp)) => 1.0 / BasicFloat::powi(base, exp as i32), (_, Err(_)) => return Err(PFE { kind: Invalid }), } }, diff --git a/traits/src/ops/checked.rs b/traits/src/ops/checked.rs index 45e6716..ce9a055 100644 --- a/traits/src/ops/checked.rs +++ b/traits/src/ops/checked.rs @@ -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 /// overflow. diff --git a/traits/src/ops/wrapping.rs b/traits/src/ops/wrapping.rs index f989058..24f3405 100644 --- a/traits/src/ops/wrapping.rs +++ b/traits/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) => { diff --git a/traits/src/pow.rs b/traits/src/pow.rs index b250ad4..212a1de 100644 --- a/traits/src/pow.rs +++ b/traits/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. diff --git a/traits/src/sign.rs b/traits/src/sign.rs index 4b43c89..0e528c1 100644 --- a/traits/src/sign.rs +++ b/traits/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; @@ -104,16 +104,15 @@ macro_rules! signed_float_impl { /// Computes the absolute value. Returns `NAN` if the number is `NAN`. #[inline] fn abs(&self) -> $t { - <$t>::abs(*self) + (*self).abs() } /// 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 @@ -123,7 +122,7 @@ macro_rules! signed_float_impl { /// - `NAN` if the number is NaN #[inline] fn signum(&self) -> $t { - <$t>::signum(*self) + (*self).signum() } /// Returns `true` if the number is positive, including `+0.0` and `INFINITY`