Make `Float` and `Real` depend on the `std` feature

We don't have implementations for many of the methods in `no_std`.  It's
hostile to external implementors if some trait methods are conditional
on a feature, as that feature could be added by anyone in a dependency
tree.  Instead, let's just live without these traits for now.
This commit is contained in:
Josh Stone 2018-01-31 15:56:06 -08:00
parent a843027b56
commit e6bb97b3ac
5 changed files with 79 additions and 19 deletions

View File

@ -1,9 +1,9 @@
use core::f64;
use core::mem::size_of; use core::mem::size_of;
use core::num::Wrapping; use core::num::Wrapping;
use identities::Zero; use identities::Zero;
use bounds::Bounded; use bounds::Bounded;
use float::Float;
/// 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 {
@ -228,7 +228,9 @@ macro_rules! impl_to_primitive_float_to_float {
// 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 = ::core::$DstT::MAX; let max_value: $DstT = ::core::$DstT::MAX;
if !Float::is_finite(n) || (-max_value as f64 <= n && n <= max_value as f64) { if n != n || n == f64::INFINITY || n == f64::NEG_INFINITY
|| (-max_value as f64 <= n && n <= max_value as f64)
{
Some($slf as $DstT) Some($slf as $DstT)
} else { } else {
None None

View File

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

View File

@ -24,7 +24,9 @@ use core::num::Wrapping;
use core::fmt; use core::fmt;
pub use bounds::Bounded; 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 real::Real; // NOTE: Don't do this, it breaks `use num_traits::*;`.
pub use identities::{Zero, One, zero, one}; pub use identities::{Zero, One, zero, one};
pub use ops::checked::*; pub use ops::checked::*;
@ -40,6 +42,7 @@ pub mod sign;
pub mod ops; pub mod ops;
pub mod bounds; pub mod bounds;
pub mod float; pub mod float;
#[cfg(feature = "std")]
pub mod real; pub mod real;
pub mod cast; pub mod cast;
pub mod int; pub mod int;
@ -187,7 +190,7 @@ impl fmt::Display for ParseFloatError {
// with this implementation ourselves until we want to make a breaking change. // with this implementation ourselves until we want to make a breaking change.
// (would have to drop it from `Num` though) // (would have to drop it from `Num` though)
macro_rules! float_trait_impl { macro_rules! float_trait_impl {
($name:ident for $($t:ty)*) => ($( ($name:ident for $($t:ident)*) => ($(
impl $name for $t { impl $name for $t {
type FromStrRadixErr = ParseFloatError; type FromStrRadixErr = ParseFloatError;
@ -199,9 +202,9 @@ macro_rules! float_trait_impl {
// Special values // Special values
match src { match src {
"inf" => return Ok(Float::infinity()), "inf" => return Ok(core::$t::INFINITY),
"-inf" => return Ok(Float::neg_infinity()), "-inf" => return Ok(core::$t::NEG_INFINITY),
"NaN" => return Ok(Float::nan()), "NaN" => return Ok(core::$t::NAN),
_ => {}, _ => {},
} }
@ -242,15 +245,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(core::$t::INFINITY); }
if !is_positive && sig >= prev_sig 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 // 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(core::$t::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(core::$t::NEG_INFINITY); }
} }
prev_sig = sig; prev_sig = sig;
}, },
@ -286,9 +289,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(core::$t::INFINITY); }
if !is_positive && sig > prev_sig if !is_positive && sig > prev_sig
{ return Ok(Float::neg_infinity()); } { return Ok(core::$t::NEG_INFINITY); }
prev_sig = sig; prev_sig = sig;
}, },
None => match c { None => match c {
@ -322,9 +325,15 @@ macro_rules! float_trait_impl {
None => return Err(PFE { kind: Invalid }), 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) { match (is_positive, exp) {
(true, Ok(exp)) => Float::powi(base, exp as i32), (true, Ok(exp)) => pow(base, exp),
(false, Ok(exp)) => 1.0 / Float::powi(base, exp as i32), (false, Ok(exp)) => 1.0 / pow(base, exp),
(_, Err(_)) => return Err(PFE { kind: Invalid }), (_, Err(_)) => return Err(PFE { kind: Invalid }),
} }
}, },

View File

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

View File

@ -2,7 +2,7 @@ use core::ops::Neg;
use core::{f32, f64}; use core::{f32, f64};
use core::num::Wrapping; use core::num::Wrapping;
use {Num, Float}; use Num;
/// Useful functions for signed numbers (i.e. numbers that can be negative). /// Useful functions for signed numbers (i.e. numbers that can be negative).
pub trait Signed: Sized + Num + Neg<Output = Self> { pub trait Signed: Sized + Num + Neg<Output = Self> {
@ -103,10 +103,24 @@ macro_rules! signed_float_impl {
impl Signed for $t { impl Signed for $t {
/// Computes the absolute value. Returns `NAN` if the number is `NAN`. /// Computes the absolute value. Returns `NAN` if the number is `NAN`.
#[inline] #[inline]
#[cfg(feature = "std")]
fn abs(&self) -> $t { fn abs(&self) -> $t {
(*self).abs() (*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 /// 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.
@ -121,10 +135,29 @@ macro_rules! signed_float_impl {
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
/// - `NAN` if the number is NaN /// - `NAN` if the number is NaN
#[inline] #[inline]
#[cfg(feature = "std")]
fn signum(&self) -> $t { fn signum(&self) -> $t {
use Float;
Float::signum(*self) Float::signum(*self)
} }
/// # 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(not(feature = "std"))]
fn signum(&self) -> $t {
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` /// Returns `true` if the number is positive, including `+0.0` and `INFINITY`
#[inline] #[inline]
fn is_positive(&self) -> bool { *self > 0.0 || (1.0 / *self) == $inf } fn is_positive(&self) -> bool { *self > 0.0 || (1.0 / *self) == $inf }