Skip to content

Commit 2f0cffd

Browse files
bors[bot]yoanlcqcuviper
authored
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]>
2 parents 4fc3d8f + 93328df commit 2f0cffd

File tree

9 files changed

+142
-36
lines changed

9 files changed

+142
-36
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ matrix:
3434
- rustup target add $TARGET
3535
script:
3636
- cargo build --verbose --target $TARGET --no-default-features --features i128
37+
- cargo build --verbose --target $TARGET --no-default-features --features libm
3738
- name: "rustfmt"
3839
rust: 1.31.0
3940
before_script:

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ exclude = ["/ci/*", "/.travis.yml", "/bors.toml"]
1717
features = ["std"]
1818

1919
[dependencies]
20+
libm = { version = "0.1.4", optional = true }
2021

2122
[features]
2223
default = ["std"]

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,14 @@ the default `std` feature. Use this in `Cargo.toml`:
3131
[dependencies.num-traits]
3232
version = "0.2"
3333
default-features = false
34+
# features = ["libm"] # <--- Uncomment if you wish to use `Float` and `Real` without `std`
3435
```
3536

36-
The `Float` and `Real` traits are only available when `std` is enabled. The
37-
`FloatCore` trait is always available. `MulAdd` and `MulAddAssign` for `f32`
38-
and `f64` also require `std`, as do implementations of signed and floating-
37+
The `Float` and `Real` traits are only available when either `std` or `libm` is enabled.
38+
The `libm` feature is only available with Rust 1.31 and later ([see PR #99](https://github.com/rust-num/num-traits/pull/99)).
39+
40+
The `FloatCore` trait is always available. `MulAdd` and `MulAddAssign` for `f32`
41+
and `f64` also require `std` or `libm`, as do implementations of signed and floating-
3942
point exponents in `Pow`.
4043

4144
Implementations for `i128` and `u128` are only available with Rust 1.26 and

ci/test_full.sh

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,16 @@ cargo test --verbose
1212
cargo build --verbose --no-default-features
1313
cargo test --verbose --no-default-features
1414

15-
# test `i128`
1615
if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable)$ ]]; then
16+
# test `i128`
1717
cargo build --verbose --features=i128
1818
cargo test --verbose --features=i128
19+
20+
# test with std and libm (libm build fails on Rust 1.26 and earlier)
21+
cargo build --verbose --features "libm"
22+
cargo test --verbose --features "libm"
23+
24+
# test `no_std` with libm (libm build fails on Rust 1.26 and earlier)
25+
cargo build --verbose --no-default-features --features "libm"
26+
cargo test --verbose --no-default-features --features "libm"
1927
fi

src/float.rs

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ use core::f64;
77

88
use {Num, NumCast, ToPrimitive};
99

10+
#[cfg(all(not(feature = "std"), feature = "libm"))]
11+
use libm::{F32Ext, F64Ext};
12+
1013
/// Generic trait for floating point numbers that works with `no_std`.
1114
///
1215
/// This trait implements a subset of the `Float` trait.
@@ -897,8 +900,8 @@ impl FloatCore for f64 {
897900

898901
/// Generic trait for floating point numbers
899902
///
900-
/// This trait is only available with the `std` feature.
901-
#[cfg(feature = "std")]
903+
/// This trait is only available with the `std` feature, or with the `libm` feature otherwise.
904+
#[cfg(any(feature = "std", feature = "libm"))]
902905
pub trait Float: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
903906
/// Returns the `NaN` value.
904907
///
@@ -1806,7 +1809,7 @@ pub trait Float: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
18061809
}
18071810

18081811
#[cfg(feature = "std")]
1809-
macro_rules! float_impl {
1812+
macro_rules! float_impl_std {
18101813
($T:ident $decode:ident) => {
18111814
impl Float for $T {
18121815
constant! {
@@ -1884,6 +1887,85 @@ macro_rules! float_impl {
18841887
};
18851888
}
18861889

1890+
#[cfg(all(not(feature = "std"), feature = "libm"))]
1891+
macro_rules! float_impl_libm {
1892+
($T:ident $decode:ident $LibmImpl:ident) => {
1893+
impl Float for $T {
1894+
constant! {
1895+
nan() -> $T::NAN;
1896+
infinity() -> $T::INFINITY;
1897+
neg_infinity() -> $T::NEG_INFINITY;
1898+
neg_zero() -> -0.0;
1899+
min_value() -> $T::MIN;
1900+
min_positive_value() -> $T::MIN_POSITIVE;
1901+
epsilon() -> $T::EPSILON;
1902+
max_value() -> $T::MAX;
1903+
}
1904+
1905+
#[inline]
1906+
#[allow(deprecated)]
1907+
fn abs_sub(self, other: Self) -> Self {
1908+
<$T as $LibmImpl>::fdim(self, other)
1909+
}
1910+
1911+
#[inline]
1912+
fn integer_decode(self) -> (u64, i16, i8) {
1913+
$decode(self)
1914+
}
1915+
1916+
forward! {
1917+
FloatCore::is_nan(self) -> bool;
1918+
FloatCore::is_infinite(self) -> bool;
1919+
FloatCore::is_finite(self) -> bool;
1920+
FloatCore::is_normal(self) -> bool;
1921+
FloatCore::classify(self) -> FpCategory;
1922+
$LibmImpl::floor(self) -> Self;
1923+
$LibmImpl::ceil(self) -> Self;
1924+
$LibmImpl::round(self) -> Self;
1925+
$LibmImpl::trunc(self) -> Self;
1926+
$LibmImpl::fract(self) -> Self;
1927+
$LibmImpl::abs(self) -> Self;
1928+
FloatCore::signum(self) -> Self;
1929+
FloatCore::is_sign_positive(self) -> bool;
1930+
FloatCore::is_sign_negative(self) -> bool;
1931+
$LibmImpl::mul_add(self, a: Self, b: Self) -> Self;
1932+
FloatCore::recip(self) -> Self;
1933+
FloatCore::powi(self, n: i32) -> Self;
1934+
$LibmImpl::powf(self, n: Self) -> Self;
1935+
$LibmImpl::sqrt(self) -> Self;
1936+
$LibmImpl::exp(self) -> Self;
1937+
$LibmImpl::exp2(self) -> Self;
1938+
$LibmImpl::ln(self) -> Self;
1939+
$LibmImpl::log(self, base: Self) -> Self;
1940+
$LibmImpl::log2(self) -> Self;
1941+
$LibmImpl::log10(self) -> Self;
1942+
FloatCore::to_degrees(self) -> Self;
1943+
FloatCore::to_radians(self) -> Self;
1944+
FloatCore::max(self, other: Self) -> Self;
1945+
FloatCore::min(self, other: Self) -> Self;
1946+
$LibmImpl::cbrt(self) -> Self;
1947+
$LibmImpl::hypot(self, other: Self) -> Self;
1948+
$LibmImpl::sin(self) -> Self;
1949+
$LibmImpl::cos(self) -> Self;
1950+
$LibmImpl::tan(self) -> Self;
1951+
$LibmImpl::asin(self) -> Self;
1952+
$LibmImpl::acos(self) -> Self;
1953+
$LibmImpl::atan(self) -> Self;
1954+
$LibmImpl::atan2(self, other: Self) -> Self;
1955+
$LibmImpl::sin_cos(self) -> (Self, Self);
1956+
$LibmImpl::exp_m1(self) -> Self;
1957+
$LibmImpl::ln_1p(self) -> Self;
1958+
$LibmImpl::sinh(self) -> Self;
1959+
$LibmImpl::cosh(self) -> Self;
1960+
$LibmImpl::tanh(self) -> Self;
1961+
$LibmImpl::asinh(self) -> Self;
1962+
$LibmImpl::acosh(self) -> Self;
1963+
$LibmImpl::atanh(self) -> Self;
1964+
}
1965+
}
1966+
};
1967+
}
1968+
18871969
fn integer_decode_f32(f: f32) -> (u64, i16, i8) {
18881970
// Safety: this identical to the implementation of f32::to_bits(),
18891971
// which is only available starting at Rust 1.20
@@ -1917,9 +1999,14 @@ fn integer_decode_f64(f: f64) -> (u64, i16, i8) {
19171999
}
19182000

19192001
#[cfg(feature = "std")]
1920-
float_impl!(f32 integer_decode_f32);
2002+
float_impl_std!(f32 integer_decode_f32);
19212003
#[cfg(feature = "std")]
1922-
float_impl!(f64 integer_decode_f64);
2004+
float_impl_std!(f64 integer_decode_f64);
2005+
2006+
#[cfg(all(not(feature = "std"), feature = "libm"))]
2007+
float_impl_libm!(f32 integer_decode_f32 F32Ext);
2008+
#[cfg(all(not(feature = "std"), feature = "libm"))]
2009+
float_impl_libm!(f64 integer_decode_f64 F64Ext);
19232010

19242011
macro_rules! float_const_impl {
19252012
($(#[$doc:meta] $constant:ident,)+) => (
@@ -2002,7 +2089,7 @@ mod tests {
20022089
}
20032090
}
20042091

2005-
#[cfg(feature = "std")]
2092+
#[cfg(any(feature = "std", feature = "libm"))]
20062093
#[test]
20072094
fn convert_deg_rad_std() {
20082095
for &(deg, rad) in &DEG_RAD_PAIRS {

src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@
2020
#[cfg(feature = "std")]
2121
extern crate std;
2222

23+
// Only `no_std` builds actually use `libm`.
24+
#[cfg(all(not(feature = "std"), feature = "libm"))]
25+
extern crate libm;
26+
2327
use core::fmt;
2428
use core::num::Wrapping;
2529
use core::ops::{Add, Div, Mul, Rem, Sub};
2630
use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
2731

2832
pub use bounds::Bounded;
29-
#[cfg(feature = "std")]
33+
#[cfg(any(feature = "std", feature = "libm"))]
3034
pub use float::Float;
3135
pub use float::FloatConst;
3236
// pub use real::{FloatCore, Real}; // NOTE: Don't do this, it breaks `use num_traits::*;`.
@@ -53,7 +57,6 @@ pub mod identities;
5357
pub mod int;
5458
pub mod ops;
5559
pub mod pow;
56-
#[cfg(feature = "std")]
5760
pub mod real;
5861
pub mod sign;
5962

src/ops/mul_add.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,23 @@ pub trait MulAddAssign<A = Self, B = Self> {
3434
fn mul_add_assign(&mut self, a: A, b: B);
3535
}
3636

37-
#[cfg(feature = "std")]
37+
#[cfg(any(feature = "std", feature = "libm"))]
3838
impl MulAdd<f32, f32> for f32 {
3939
type Output = Self;
4040

4141
#[inline]
4242
fn mul_add(self, a: Self, b: Self) -> Self::Output {
43-
f32::mul_add(self, a, b)
43+
<Self as ::Float>::mul_add(self, a, b)
4444
}
4545
}
4646

47-
#[cfg(feature = "std")]
47+
#[cfg(any(feature = "std", feature = "libm"))]
4848
impl MulAdd<f64, f64> for f64 {
4949
type Output = Self;
5050

5151
#[inline]
5252
fn mul_add(self, a: Self, b: Self) -> Self::Output {
53-
f64::mul_add(self, a, b)
53+
<Self as ::Float>::mul_add(self, a, b)
5454
}
5555
}
5656

@@ -71,19 +71,19 @@ mul_add_impl!(MulAdd for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
7171
#[cfg(has_i128)]
7272
mul_add_impl!(MulAdd for i128 u128);
7373

74-
#[cfg(feature = "std")]
74+
#[cfg(any(feature = "std", feature = "libm"))]
7575
impl MulAddAssign<f32, f32> for f32 {
7676
#[inline]
7777
fn mul_add_assign(&mut self, a: Self, b: Self) {
78-
*self = f32::mul_add(*self, a, b)
78+
*self = <Self as ::Float>::mul_add(*self, a, b)
7979
}
8080
}
8181

82-
#[cfg(feature = "std")]
82+
#[cfg(any(feature = "std", feature = "libm"))]
8383
impl MulAddAssign<f64, f64> for f64 {
8484
#[inline]
8585
fn mul_add_assign(&mut self, a: Self, b: Self) {
86-
*self = f64::mul_add(*self, a, b)
86+
*self = <Self as ::Float>::mul_add(*self, a, b)
8787
}
8888
}
8989

src/pow.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -152,23 +152,24 @@ pow_impl!(Wrapping<isize>);
152152
// pow_impl!(usize, u64);
153153
// pow_impl!(isize, u64);
154154

155-
#[cfg(feature = "std")]
155+
#[cfg(any(feature = "std", feature = "libm"))]
156156
mod float_impls {
157157
use super::Pow;
158+
use Float;
158159

159-
pow_impl!(f32, i8, i32, f32::powi);
160-
pow_impl!(f32, u8, i32, f32::powi);
161-
pow_impl!(f32, i16, i32, f32::powi);
162-
pow_impl!(f32, u16, i32, f32::powi);
163-
pow_impl!(f32, i32, i32, f32::powi);
164-
pow_impl!(f64, i8, i32, f64::powi);
165-
pow_impl!(f64, u8, i32, f64::powi);
166-
pow_impl!(f64, i16, i32, f64::powi);
167-
pow_impl!(f64, u16, i32, f64::powi);
168-
pow_impl!(f64, i32, i32, f64::powi);
169-
pow_impl!(f32, f32, f32, f32::powf);
170-
pow_impl!(f64, f32, f64, f64::powf);
171-
pow_impl!(f64, f64, f64, f64::powf);
160+
pow_impl!(f32, i8, i32, <f32 as Float>::powi);
161+
pow_impl!(f32, u8, i32, <f32 as Float>::powi);
162+
pow_impl!(f32, i16, i32, <f32 as Float>::powi);
163+
pow_impl!(f32, u16, i32, <f32 as Float>::powi);
164+
pow_impl!(f32, i32, i32, <f32 as Float>::powi);
165+
pow_impl!(f64, i8, i32, <f64 as Float>::powi);
166+
pow_impl!(f64, u8, i32, <f64 as Float>::powi);
167+
pow_impl!(f64, i16, i32, <f64 as Float>::powi);
168+
pow_impl!(f64, u16, i32, <f64 as Float>::powi);
169+
pow_impl!(f64, i32, i32, <f64 as Float>::powi);
170+
pow_impl!(f32, f32, f32, <f32 as Float>::powf);
171+
pow_impl!(f64, f32, f64, <f64 as Float>::powf);
172+
pow_impl!(f64, f64, f64, <f64 as Float>::powf);
172173
}
173174

174175
/// Raises a value to the power of exp, using exponentiation by squaring.

src/real.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use std::ops::Neg;
1+
#![cfg(any(feature = "std", feature = "libm"))]
2+
3+
use core::ops::Neg;
24

35
use {Float, Num, NumCast};
46

@@ -11,7 +13,7 @@ use {Float, Num, NumCast};
1113
/// See [this Wikipedia article](https://en.wikipedia.org/wiki/Real_data_type)
1214
/// for a list of data types that could meaningfully implement this trait.
1315
///
14-
/// This trait is only available with the `std` feature.
16+
/// This trait is only available with the `std` feature, or with the `libm` feature otherwise.
1517
pub trait Real: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
1618
/// Returns the smallest finite value that this type can represent.
1719
///

0 commit comments

Comments
 (0)