Skip to content

Revive Float+Real in no_std thanks to libm #99

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Sep 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,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