Skip to content
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

fix #283: add possibility to include num-traits/libm for sin and cos … #309

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/ci-full-test-suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
run: cargo test --verbose --no-default-features --features "f32 si"

- name: Test all non-storage type features
run: cargo test --verbose --no-default-features --features "autoconvert f32 si use_serde"
run: cargo test --verbose --no-default-features --features "autoconvert f32 si use_serde libm"

- name: Test si with underlying storage types
run: cargo test --verbose --no-run --no-default-features --features "autoconvert usize isize bigint bigrational complex32 si std use_serde"
Expand Down
17 changes: 10 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,23 @@ features = ["usize", "u32", "u64", "isize", "i32", "i64", "bigint", "biguint", "
maintenance = { status = "actively-developed" }

[workspace]
members = [
"tests/feature_check",
"uom-macros",
"tests/edition_check",
]
members = ["tests/feature_check", "uom-macros", "tests/edition_check"]

[dependencies]
num-traits = { version = "0.2", default-features = false }
num-rational = { version = "0.4", optional = true, default-features = false }
num-bigint = { version = "0.4", optional = true, default-features = false, features = ["std"] }
num-complex = { version = "0.4", optional = true, default-features = false, features = ["std"] }
num-bigint = { version = "0.4", optional = true, default-features = false, features = [
"std",
] }
num-complex = { version = "0.4", optional = true, default-features = false, features = [
"std",
] }
serde = { version = "1.0", optional = true, default-features = false }
typenum = "1.13"

[dev-dependencies]
approx = "0.5"
libm = { git = "https://github.com/moritz-meier/libm.git", branch = "feature/add-generic-helper" }
quickcheck = "1.0"
serde_json = "1.0"
static_assertions = "1.1"
Expand Down Expand Up @@ -81,6 +82,8 @@ use_serde = ["serde"]
rational-support = ["num-rational"]
bigint-support = ["num-bigint", "num-rational/num-bigint-std"]
complex-support = ["num-complex"]
# enable sin/cos for no_std builds, via libm
libm = ["num-traits/libm"]

[[example]]
name = "base"
Expand Down
12 changes: 6 additions & 6 deletions src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,19 @@ macro_rules! si {
($($tt:tt)*) => {};
}

/// Expands the given block of code when `uom` is compiled with the `std` feature.
/// Expands the given block of code when `uom` is compiled with either the `std` or the `libm` feature.
#[doc(hidden)]
#[macro_export]
#[cfg(feature = "std")]
macro_rules! std {
#[cfg(any(feature = "std", feature = "libm"))]
macro_rules! std_or_libm {
($($tt:tt)*) => { $($tt)* };
}

/// Does not expand the given block of code when `uom` is compiled without the `std` feature.
/// Does not expand the given block of code when `uom` is compiled without both the `std` and the `libm` feature.
#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "std"))]
macro_rules! std {
#[cfg(not(any(feature = "std", feature = "libm")))]
macro_rules! std_or_libm {
($($tt:tt)*) => {};
}

Expand Down
5 changes: 3 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
//! default.
//! * `std` -- Feature to compile with standard library support. Disabling this feature compiles
//! `uom` with `no_std`. Enabled by default.
//! * `libm` -- Enable advanced floatingpoint functions (sin, cos, ...) on no_std targets
//! * `serde` -- Feature to enable support for serialization and deserialization of quantities with
//! the [Serde][serde] crate. Disabled by default.
//!
Expand Down Expand Up @@ -268,9 +269,9 @@ pub mod lib {
// Conditionally import num sub-crate types based on feature selection.
#[doc(hidden)]
pub mod num {
#[cfg(feature = "std")]
#[cfg(any(feature = "std", feature = "libm"))]
pub use num_traits::float::Float;
#[cfg(not(feature = "std"))]
#[cfg(not(any(feature = "std", feature = "libm")))]
pub use num_traits::float::FloatCore as Float;

pub use num_traits::{pow, FromPrimitive, Num, One, Saturating, Signed, ToPrimitive, Zero};
Expand Down
15 changes: 10 additions & 5 deletions src/si/angle.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Angle (dimensionless quantity).

#[cfg(feature = "std")]
#[cfg(any(feature = "std", feature = "libm"))]
use super::ratio::Ratio;

quantity! {
Expand Down Expand Up @@ -64,7 +64,7 @@ impl Angle<crate::si::SI<f64>, f64> {
}

/// Implementation of various stdlib trigonometric functions
#[cfg(feature = "std")]
#[cfg(any(feature = "std", feature = "libm"))]
impl<U, V> Angle<U, V>
where
U: crate::si::Units<V> + ?Sized,
Expand Down Expand Up @@ -121,7 +121,7 @@ where
}
}

#[cfg(feature = "std")]
#[cfg(any(feature = "std", feature = "libm"))]
impl<D, U, V> crate::si::Quantity<D, U, V>
where
D: crate::si::Dimension + ?Sized,
Expand Down Expand Up @@ -161,7 +161,7 @@ mod tests {
}
}

#[cfg(feature = "std")]
#[cfg(any(feature = "std", feature = "libm"))]
mod trig {
storage_types! {
types: Float;
Expand Down Expand Up @@ -216,7 +216,12 @@ mod tests {
quickcheck! {
#[allow(trivial_casts)]
fn atan2(y: V, x: V) -> bool {
Test::eq(&y.atan2(x),
let desired_value = if cfg!(feature = "libm"){
libm::Libm::<V>::atan2(y, x)
} else {
y.atan2(x)
};
Test::eq(&desired_value,
&Length::new::<l::meter>(y).atan2(Length::new::<l::meter>(x)).get::<a::radian>())
}
}
Expand Down
106 changes: 88 additions & 18 deletions src/si/ratio.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Ratio (dimensionless quantity).

#[cfg(feature = "std")]
use super::angle::{Angle, radian};
#[cfg(any(feature = "std", feature = "libm"))]
use super::angle::{radian, Angle};

quantity! {
/// Ratio (dimensionless quantity).
Expand Down Expand Up @@ -34,7 +34,7 @@ quantity! {
}

/// Implementation of various stdlib functions.
#[cfg(feature = "std")]
#[cfg(any(feature = "std", feature = "libm"))]
impl<U, V> Ratio<U, V>
where
U: crate::si::Units<V> + ?Sized,
Expand Down Expand Up @@ -221,7 +221,7 @@ mod tests {
}
}

#[cfg(feature = "std")]
#[cfg(any(feature = "std", feature = "libm"))]
mod float {
storage_types! {
types: Float;
Expand All @@ -233,59 +233,129 @@ mod tests {

quickcheck! {
fn acos(x: V) -> bool {
Test::eq(&x.acos(), &Ratio::from(x).acos().get::<a::radian>())
let desired_value = if cfg!(feature = "libm") {
libm::Libm::<V>::acos(x)
} else {
x.acos()
};
Test::eq(&desired_value, &Ratio::from(x).acos().get::<a::radian>())
}

fn acosh(x: V) -> bool {
Test::eq(&x.acosh(), &Ratio::from(x).acosh().get::<a::radian>())
let desired_value = if cfg!(feature = "libm") {
libm::Libm::<V>::acosh(x)
} else {
x.acosh()
};
Test::eq(&desired_value, &Ratio::from(x).acosh().get::<a::radian>())
}

fn asin(x: V) -> bool {
Test::eq(&x.asin(), &Ratio::from(x).asin().get::<a::radian>())
let desired_value = if cfg!(feature = "libm") {
libm::Libm::<V>::asin(x)
} else {
x.asin()
};
Test::eq(&desired_value, &Ratio::from(x).asin().get::<a::radian>())
}

fn asinh(x: V) -> bool {
Test::eq(&x.asinh(), &Ratio::from(x).asinh().get::<a::radian>())
let desired_value = if cfg!(feature = "libm") {
libm::Libm::<V>::asinh(x)
} else {
x.asinh()
};
Test::eq(&desired_value, &Ratio::from(x).asinh().get::<a::radian>())
}

fn atan(x: V) -> bool {
Test::eq(&x.atan(), &Ratio::from(x).atan().get::<a::radian>())
let desired_value = if cfg!(feature = "libm") {
libm::Libm::<V>::atan(x)
} else {
x.atan()
};
Test::eq(&desired_value, &Ratio::from(x).atan().get::<a::radian>())
}

fn atanh(x: V) -> bool {
Test::eq(&x.atanh(), &Ratio::from(x).atanh().get::<a::radian>())
let desired_value = if cfg!(feature = "libm") {
libm::Libm::<V>::atanh(x)
} else {
x.atanh()
};
Test::eq(&desired_value, &Ratio::from(x).atanh().get::<a::radian>())
}

fn exp(x: V) -> bool {
Test::eq(&x.exp(), &Ratio::from(x).exp().get::<r::ratio>())
let desired_value = if cfg!(feature = "libm"){
libm::Libm::<V>::exp(x)
} else {
x.exp()
};
Test::eq(&desired_value, &Ratio::from(x).exp().get::<r::ratio>())
}

fn exp2(x: V) -> bool {
Test::eq(&x.exp2(), &Ratio::from(x).exp2().get::<r::ratio>())
let desired_value = if cfg!(feature = "libm"){
libm::Libm::<V>::exp2(x)
} else {
x.exp2()
};
Test::eq(&desired_value, &Ratio::from(x).exp2().get::<r::ratio>())
}

fn ln(x: V) -> bool {
Test::eq(&x.ln(), &Ratio::from(x).ln().get::<r::ratio>())
let desired_value = if cfg!(feature = "libm"){
libm::Libm::<V>::log(x)
} else {
x.ln()
};
Test::eq(&desired_value, &Ratio::from(x).ln().get::<r::ratio>())
}

fn log(x: V, y: V) -> bool {
Test::eq(&x.log(y), &Ratio::from(x).log(y).get::<r::ratio>())
let desired_value = if cfg!(feature = "libm"){
libm::Libm::<V>::log(x) / libm::Libm::<V>::log(y)
} else {
x.log(y)
};
Test::eq(&desired_value, &Ratio::from(x).log(y).get::<r::ratio>())
}

fn log2(x: V) -> bool {
Test::eq(&x.log2(), &Ratio::from(x).log2().get::<r::ratio>())
let desired_value = if cfg!(feature = "libm"){
libm::Libm::<V>::log2(x)
} else {
x.log2()
};
Test::eq(&desired_value, &Ratio::from(x).log2().get::<r::ratio>())
}

fn log10(x: V) -> bool {
Test::eq(&x.log10(), &Ratio::from(x).log10().get::<r::ratio>())
let desired_value = if cfg!(feature = "libm"){
libm::Libm::<V>::log10(x)
} else {
x.log10()
};
Test::eq(&desired_value, &Ratio::from(x).log10().get::<r::ratio>())
}

fn exp_m1(x: V) -> bool {
Test::eq(&x.exp_m1(), &Ratio::from(x).exp_m1().get::<r::ratio>())
let desired_value = if cfg!(feature = "libm"){
libm::Libm::<V>::expm1(x)
} else {
x.exp_m1()
};
Test::eq(&desired_value, &Ratio::from(x).exp_m1().get::<r::ratio>())
}

fn ln_1p(x: V) -> bool {
Test::eq(&x.ln_1p(), &Ratio::from(x).ln_1p().get::<r::ratio>())
let desired_value = if cfg!(feature = "libm"){
libm::Libm::<V>::log1p(x)
} else {
x.ln_1p()
};
Test::eq(&desired_value, &Ratio::from(x).ln_1p().get::<r::ratio>())
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ macro_rules! system {
self.value.classify()
}

std! {
std_or_libm! {
autoconvert! {
/// Calculates the length of the hypotenuse of a right-angle triangle given the legs.
#[must_use = "method returns a new number and does not mutate the original value"]
Expand Down Expand Up @@ -807,7 +807,7 @@ macro_rules! system {
self.value.is_normal()
}

std! {
std_or_libm! {
/// Takes the cubic root of a number.
///
#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]
Expand Down Expand Up @@ -841,7 +841,7 @@ macro_rules! system {
Quantity {
dimension: $crate::lib::marker::PhantomData,
units: $crate::lib::marker::PhantomData,
value: self.value.cbrt(),
value: $crate::num::Float::cbrt(self.value),
}
}

Expand Down Expand Up @@ -907,7 +907,7 @@ macro_rules! system {
Quantity {
dimension: $crate::lib::marker::PhantomData,
units: $crate::lib::marker::PhantomData,
value: self.value.powi(E::to_i32()),
value: $crate::num::Float::powi(self.value, E::to_i32()),
}
}

Expand Down Expand Up @@ -945,7 +945,7 @@ macro_rules! system {
Quantity {
dimension: $crate::lib::marker::PhantomData,
units: $crate::lib::marker::PhantomData,
value: self.value.sqrt(),
value: $crate::num::Float::sqrt(self.value),
}
}}
}
Expand Down
11 changes: 8 additions & 3 deletions src/tests/quantity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,16 +468,21 @@ mod float {
Test::assert_eq(&3.3.fract(), &m1.fract::<kilogram>().get::<kilogram>());
}

#[cfg(feature = "std")]
#[cfg(any(feature = "std", feature = "libm"))]
quickcheck! {
#[allow(trivial_casts)]
fn hypot_same(l: V, r: V) -> bool {
Test::eq(&l.hypot(r),
let desired_value = if cfg!(feature="libm") && ! cfg!(feature="std") {
libm::Libm::<V>::hypot(l, r)
} else {
l.hypot(r)
};
Test::eq(&desired_value,
&f::Length::new::<meter>(l).hypot(f::Length::new::<meter>(r)).get::<meter>())
}
}

#[cfg(all(feature = "std", feature = "autoconvert"))]
#[cfg(all(any(feature = "std", feature = "libm"), feature = "autoconvert"))]
quickcheck! {
#[allow(trivial_casts)]
fn hypot_mixed(l: V, r: V) -> bool {
Expand Down
Loading