瀏覽代碼

Merge #99

99: Revive Float+Real in no_std thanks to libm r=cuviper a=yoanlcq

Greetings,

This is a hopeful fix for #75.  
Basically: Add `libm` as an optional dependency, and handle three possible cases depending on which features are enabled:
- std and libm: std is used;
- std and not libm: std is used;
- libm and not std: libm and FloatCore are used.

It was briefly mentioned that `libm` wasn't ready yet, but this was months ago, and I believe it is better not to wait for too long.  
If anything, bugs in `libm` should be fixed in `libm`; `num-traits` is only delegating its implementations to it; not to mention that the more `libm` is used, the likelier issues are to be found and hopefully fixed.

Thanks in advance!

Co-authored-by: Yoan Lecoq <[email protected]>
Co-authored-by: Josh Stone <[email protected]>
bors[bot] 5 年之前
父節點
當前提交
2f0cffd522
共有 9 個文件被更改,包括 142 次插入36 次删除
  1. 1 0
      .travis.yml
  2. 1 0
      Cargo.toml
  3. 6 3
      README.md
  4. 9 1
      ci/test_full.sh
  5. 93 6
      src/float.rs
  6. 5 2
      src/lib.rs
  7. 8 8
      src/ops/mul_add.rs
  8. 15 14
      src/pow.rs
  9. 4 2
      src/real.rs

+ 1 - 0
.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:

+ 1 - 0
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"]

+ 6 - 3
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

+ 9 - 1
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

+ 93 - 6
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<Output = Self> {
     /// Returns the `NaN` value.
     ///
@@ -1806,7 +1809,7 @@ pub trait Float: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
 }
 
 #[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 {

+ 5 - 2
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;
 

+ 8 - 8
src/ops/mul_add.rs

@@ -34,23 +34,23 @@ pub trait MulAddAssign<A = Self, B = Self> {
     fn mul_add_assign(&mut self, a: A, b: B);
 }
 
-#[cfg(feature = "std")]
+#[cfg(any(feature = "std", feature = "libm"))]
 impl MulAdd<f32, f32> for f32 {
     type Output = Self;
 
     #[inline]
     fn mul_add(self, a: Self, b: Self) -> Self::Output {
-        f32::mul_add(self, a, b)
+        <Self as ::Float>::mul_add(self, a, b)
     }
 }
 
-#[cfg(feature = "std")]
+#[cfg(any(feature = "std", feature = "libm"))]
 impl MulAdd<f64, f64> for f64 {
     type Output = Self;
 
     #[inline]
     fn mul_add(self, a: Self, b: Self) -> Self::Output {
-        f64::mul_add(self, a, b)
+        <Self as ::Float>::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<f32, f32> for f32 {
     #[inline]
     fn mul_add_assign(&mut self, a: Self, b: Self) {
-        *self = f32::mul_add(*self, a, b)
+        *self = <Self as ::Float>::mul_add(*self, a, b)
     }
 }
 
-#[cfg(feature = "std")]
+#[cfg(any(feature = "std", feature = "libm"))]
 impl MulAddAssign<f64, f64> for f64 {
     #[inline]
     fn mul_add_assign(&mut self, a: Self, b: Self) {
-        *self = f64::mul_add(*self, a, b)
+        *self = <Self as ::Float>::mul_add(*self, a, b)
     }
 }
 

+ 15 - 14
src/pow.rs

@@ -152,23 +152,24 @@ pow_impl!(Wrapping<isize>);
 // 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, <f32 as Float>::powi);
+    pow_impl!(f32, u8, i32, <f32 as Float>::powi);
+    pow_impl!(f32, i16, i32, <f32 as Float>::powi);
+    pow_impl!(f32, u16, i32, <f32 as Float>::powi);
+    pow_impl!(f32, i32, i32, <f32 as Float>::powi);
+    pow_impl!(f64, i8, i32, <f64 as Float>::powi);
+    pow_impl!(f64, u8, i32, <f64 as Float>::powi);
+    pow_impl!(f64, i16, i32, <f64 as Float>::powi);
+    pow_impl!(f64, u16, i32, <f64 as Float>::powi);
+    pow_impl!(f64, i32, i32, <f64 as Float>::powi);
+    pow_impl!(f32, f32, f32, <f32 as Float>::powf);
+    pow_impl!(f64, f32, f64, <f64 as Float>::powf);
+    pow_impl!(f64, f64, f64, <f64 as Float>::powf);
 }
 
 /// Raises a value to the power of exp, using exponentiation by squaring.

+ 4 - 2
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<Output = Self> {
     /// Returns the smallest finite value that this type can represent.
     ///