Skip to content

Commit

Permalink
Merge #99
Browse files Browse the repository at this point in the history
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 <yoanlecoq.io@gmail.com>
Co-authored-by: Josh Stone <cuviper@gmail.com>
  • Loading branch information
3 people authored Sep 30, 2019
2 parents 4fc3d8f + 93328df commit 2f0cffd
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 36 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ exclude = ["/ci/*", "/.travis.yml", "/bors.toml"]
features = ["std"]

[dependencies]
libm = { version = "0.1.4", optional = true }

[features]
default = ["std"]
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion ci/test_full.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
99 changes: 93 additions & 6 deletions src/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -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! {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,)+) => (
Expand Down Expand Up @@ -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 {
Expand Down
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;`.
Expand All @@ -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;

Expand Down
16 changes: 8 additions & 8 deletions src/ops/mul_add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand All @@ -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)
}
}

Expand Down
29 changes: 15 additions & 14 deletions src/pow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 4 additions & 2 deletions src/real.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use std::ops::Neg;
#![cfg(any(feature = "std", feature = "libm"))]

use core::ops::Neg;

use {Float, Num, NumCast};

Expand All @@ -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.
///
Expand Down

0 comments on commit 2f0cffd

Please sign in to comment.