diff --git a/.travis.yml b/.travis.yml index d7e87af..c2446e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ matrix: - rustup target add $TARGET script: - cargo build --verbose --target $TARGET --no-default-features --features i128 + - cargo build --verbose --target $TARGET --no-default-features --features libm - name: "rustfmt" rust: 1.31.0 before_script: diff --git a/Cargo.toml b/Cargo.toml index b673280..0d33030 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ exclude = ["/ci/*", "/.travis.yml", "/bors.toml"] features = ["std"] [dependencies] +libm = { version = "0.1.4", optional = true } [features] default = ["std"] diff --git a/README.md b/README.md index d2ea0d0..43335b1 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,14 @@ the default `std` feature. Use this in `Cargo.toml`: [dependencies.num-traits] version = "0.2" default-features = false +# features = ["libm"] # <--- Uncomment if you wish to use `Float` and `Real` without `std` ``` -The `Float` and `Real` traits are only available when `std` is enabled. The -`FloatCore` trait is always available. `MulAdd` and `MulAddAssign` for `f32` -and `f64` also require `std`, as do implementations of signed and floating- +The `Float` and `Real` traits are only available when either `std` or `libm` is enabled. +The `libm` feature is only available with Rust 1.31 and later ([see PR #99](https://github.com/rust-num/num-traits/pull/99)). + +The `FloatCore` trait is always available. `MulAdd` and `MulAddAssign` for `f32` +and `f64` also require `std` or `libm`, as do implementations of signed and floating- point exponents in `Pow`. Implementations for `i128` and `u128` are only available with Rust 1.26 and diff --git a/ci/test_full.sh b/ci/test_full.sh index 08632d4..ca6fce0 100755 --- a/ci/test_full.sh +++ b/ci/test_full.sh @@ -12,8 +12,16 @@ cargo test --verbose cargo build --verbose --no-default-features cargo test --verbose --no-default-features -# test `i128` if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable)$ ]]; then + # test `i128` cargo build --verbose --features=i128 cargo test --verbose --features=i128 + + # test with std and libm (libm build fails on Rust 1.26 and earlier) + cargo build --verbose --features "libm" + cargo test --verbose --features "libm" + + # test `no_std` with libm (libm build fails on Rust 1.26 and earlier) + cargo build --verbose --no-default-features --features "libm" + cargo test --verbose --no-default-features --features "libm" fi diff --git a/src/float.rs b/src/float.rs index b0f773d..6f12b56 100644 --- a/src/float.rs +++ b/src/float.rs @@ -7,6 +7,9 @@ use core::f64; use {Num, NumCast, ToPrimitive}; +#[cfg(all(not(feature = "std"), feature = "libm"))] +use libm::{F32Ext, F64Ext}; + /// Generic trait for floating point numbers that works with `no_std`. /// /// This trait implements a subset of the `Float` trait. @@ -897,8 +900,8 @@ impl FloatCore for f64 { /// Generic trait for floating point numbers /// -/// This trait is only available with the `std` feature. -#[cfg(feature = "std")] +/// This trait is only available with the `std` feature, or with the `libm` feature otherwise. +#[cfg(any(feature = "std", feature = "libm"))] pub trait Float: Num + Copy + NumCast + PartialOrd + Neg { /// Returns the `NaN` value. /// @@ -1806,7 +1809,7 @@ pub trait Float: Num + Copy + NumCast + PartialOrd + Neg { } #[cfg(feature = "std")] -macro_rules! float_impl { +macro_rules! float_impl_std { ($T:ident $decode:ident) => { impl Float for $T { constant! { @@ -1884,6 +1887,85 @@ macro_rules! float_impl { }; } +#[cfg(all(not(feature = "std"), feature = "libm"))] +macro_rules! float_impl_libm { + ($T:ident $decode:ident $LibmImpl:ident) => { + impl Float for $T { + constant! { + nan() -> $T::NAN; + infinity() -> $T::INFINITY; + neg_infinity() -> $T::NEG_INFINITY; + neg_zero() -> -0.0; + min_value() -> $T::MIN; + min_positive_value() -> $T::MIN_POSITIVE; + epsilon() -> $T::EPSILON; + max_value() -> $T::MAX; + } + + #[inline] + #[allow(deprecated)] + fn abs_sub(self, other: Self) -> Self { + <$T as $LibmImpl>::fdim(self, other) + } + + #[inline] + fn integer_decode(self) -> (u64, i16, i8) { + $decode(self) + } + + forward! { + FloatCore::is_nan(self) -> bool; + FloatCore::is_infinite(self) -> bool; + FloatCore::is_finite(self) -> bool; + FloatCore::is_normal(self) -> bool; + FloatCore::classify(self) -> FpCategory; + $LibmImpl::floor(self) -> Self; + $LibmImpl::ceil(self) -> Self; + $LibmImpl::round(self) -> Self; + $LibmImpl::trunc(self) -> Self; + $LibmImpl::fract(self) -> Self; + $LibmImpl::abs(self) -> Self; + FloatCore::signum(self) -> Self; + FloatCore::is_sign_positive(self) -> bool; + FloatCore::is_sign_negative(self) -> bool; + $LibmImpl::mul_add(self, a: Self, b: Self) -> Self; + FloatCore::recip(self) -> Self; + FloatCore::powi(self, n: i32) -> Self; + $LibmImpl::powf(self, n: Self) -> Self; + $LibmImpl::sqrt(self) -> Self; + $LibmImpl::exp(self) -> Self; + $LibmImpl::exp2(self) -> Self; + $LibmImpl::ln(self) -> Self; + $LibmImpl::log(self, base: Self) -> Self; + $LibmImpl::log2(self) -> Self; + $LibmImpl::log10(self) -> Self; + FloatCore::to_degrees(self) -> Self; + FloatCore::to_radians(self) -> Self; + FloatCore::max(self, other: Self) -> Self; + FloatCore::min(self, other: Self) -> Self; + $LibmImpl::cbrt(self) -> Self; + $LibmImpl::hypot(self, other: Self) -> Self; + $LibmImpl::sin(self) -> Self; + $LibmImpl::cos(self) -> Self; + $LibmImpl::tan(self) -> Self; + $LibmImpl::asin(self) -> Self; + $LibmImpl::acos(self) -> Self; + $LibmImpl::atan(self) -> Self; + $LibmImpl::atan2(self, other: Self) -> Self; + $LibmImpl::sin_cos(self) -> (Self, Self); + $LibmImpl::exp_m1(self) -> Self; + $LibmImpl::ln_1p(self) -> Self; + $LibmImpl::sinh(self) -> Self; + $LibmImpl::cosh(self) -> Self; + $LibmImpl::tanh(self) -> Self; + $LibmImpl::asinh(self) -> Self; + $LibmImpl::acosh(self) -> Self; + $LibmImpl::atanh(self) -> Self; + } + } + }; +} + fn integer_decode_f32(f: f32) -> (u64, i16, i8) { // Safety: this identical to the implementation of f32::to_bits(), // which is only available starting at Rust 1.20 @@ -1917,9 +1999,14 @@ fn integer_decode_f64(f: f64) -> (u64, i16, i8) { } #[cfg(feature = "std")] -float_impl!(f32 integer_decode_f32); +float_impl_std!(f32 integer_decode_f32); #[cfg(feature = "std")] -float_impl!(f64 integer_decode_f64); +float_impl_std!(f64 integer_decode_f64); + +#[cfg(all(not(feature = "std"), feature = "libm"))] +float_impl_libm!(f32 integer_decode_f32 F32Ext); +#[cfg(all(not(feature = "std"), feature = "libm"))] +float_impl_libm!(f64 integer_decode_f64 F64Ext); macro_rules! float_const_impl { ($(#[$doc:meta] $constant:ident,)+) => ( @@ -2002,7 +2089,7 @@ mod tests { } } - #[cfg(feature = "std")] + #[cfg(any(feature = "std", feature = "libm"))] #[test] fn convert_deg_rad_std() { for &(deg, rad) in &DEG_RAD_PAIRS { diff --git a/src/lib.rs b/src/lib.rs index 73fcdb3..559a18c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,13 +20,17 @@ #[cfg(feature = "std")] extern crate std; +// Only `no_std` builds actually use `libm`. +#[cfg(all(not(feature = "std"), feature = "libm"))] +extern crate libm; + use core::fmt; use core::num::Wrapping; use core::ops::{Add, Div, Mul, Rem, Sub}; use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; pub use bounds::Bounded; -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "libm"))] pub use float::Float; pub use float::FloatConst; // pub use real::{FloatCore, Real}; // NOTE: Don't do this, it breaks `use num_traits::*;`. @@ -53,7 +57,6 @@ pub mod identities; pub mod int; pub mod ops; pub mod pow; -#[cfg(feature = "std")] pub mod real; pub mod sign; diff --git a/src/ops/mul_add.rs b/src/ops/mul_add.rs index 1e6bfeb..c5835d3 100644 --- a/src/ops/mul_add.rs +++ b/src/ops/mul_add.rs @@ -34,23 +34,23 @@ pub trait MulAddAssign { fn mul_add_assign(&mut self, a: A, b: B); } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "libm"))] impl MulAdd for f32 { type Output = Self; #[inline] fn mul_add(self, a: Self, b: Self) -> Self::Output { - f32::mul_add(self, a, b) + ::mul_add(self, a, b) } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "libm"))] impl MulAdd for f64 { type Output = Self; #[inline] fn mul_add(self, a: Self, b: Self) -> Self::Output { - f64::mul_add(self, a, b) + ::mul_add(self, a, b) } } @@ -71,19 +71,19 @@ mul_add_impl!(MulAdd for isize usize i8 u8 i16 u16 i32 u32 i64 u64); #[cfg(has_i128)] mul_add_impl!(MulAdd for i128 u128); -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "libm"))] impl MulAddAssign for f32 { #[inline] fn mul_add_assign(&mut self, a: Self, b: Self) { - *self = f32::mul_add(*self, a, b) + *self = ::mul_add(*self, a, b) } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "libm"))] impl MulAddAssign for f64 { #[inline] fn mul_add_assign(&mut self, a: Self, b: Self) { - *self = f64::mul_add(*self, a, b) + *self = ::mul_add(*self, a, b) } } diff --git a/src/pow.rs b/src/pow.rs index daf7b41..8addc21 100644 --- a/src/pow.rs +++ b/src/pow.rs @@ -152,23 +152,24 @@ pow_impl!(Wrapping); // pow_impl!(usize, u64); // pow_impl!(isize, u64); -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "libm"))] mod float_impls { use super::Pow; + use Float; - pow_impl!(f32, i8, i32, f32::powi); - pow_impl!(f32, u8, i32, f32::powi); - pow_impl!(f32, i16, i32, f32::powi); - pow_impl!(f32, u16, i32, f32::powi); - pow_impl!(f32, i32, i32, f32::powi); - pow_impl!(f64, i8, i32, f64::powi); - pow_impl!(f64, u8, i32, f64::powi); - pow_impl!(f64, i16, i32, f64::powi); - pow_impl!(f64, u16, i32, f64::powi); - pow_impl!(f64, i32, i32, f64::powi); - pow_impl!(f32, f32, f32, f32::powf); - pow_impl!(f64, f32, f64, f64::powf); - pow_impl!(f64, f64, f64, f64::powf); + pow_impl!(f32, i8, i32, ::powi); + pow_impl!(f32, u8, i32, ::powi); + pow_impl!(f32, i16, i32, ::powi); + pow_impl!(f32, u16, i32, ::powi); + pow_impl!(f32, i32, i32, ::powi); + pow_impl!(f64, i8, i32, ::powi); + pow_impl!(f64, u8, i32, ::powi); + pow_impl!(f64, i16, i32, ::powi); + pow_impl!(f64, u16, i32, ::powi); + pow_impl!(f64, i32, i32, ::powi); + pow_impl!(f32, f32, f32, ::powf); + pow_impl!(f64, f32, f64, ::powf); + pow_impl!(f64, f64, f64, ::powf); } /// Raises a value to the power of exp, using exponentiation by squaring. diff --git a/src/real.rs b/src/real.rs index 23ac6b8..8b31cce 100644 --- a/src/real.rs +++ b/src/real.rs @@ -1,4 +1,6 @@ -use std::ops::Neg; +#![cfg(any(feature = "std", feature = "libm"))] + +use core::ops::Neg; use {Float, Num, NumCast}; @@ -11,7 +13,7 @@ use {Float, Num, NumCast}; /// 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. +/// This trait is only available with the `std` feature, or with the `libm` feature otherwise. pub trait Real: Num + Copy + NumCast + PartialOrd + Neg { /// Returns the smallest finite value that this type can represent. ///