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

Add support for micromath #415

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "uom"
version = "0.34.0"
version = "0.34.1"
edition = "2018"
authors = ["Mike Boutin <mike.boutin@gmail.com>"]
description = "Units of measurement"
Expand Down Expand Up @@ -39,6 +39,7 @@ num-bigint = { version = "0.4", optional = true, default-features = false, featu
num-complex = { version = "0.4", optional = true, default-features = false, features = ["std"] }
serde = { version = "1.0", optional = true, default-features = false }
typenum = "1.13"
micromath = {version = "2.0.0", optional = true, features = ["num-traits"]}

[dev-dependencies]
approx = "0.5"
Expand Down Expand Up @@ -73,6 +74,7 @@ f32 = []
f64 = []
si = []
std = ["num-traits/std"]
use_micromath = ["micromath"]
# The try-from feature is deprecated and will be removed in a future release of uom. Functionality
# previously exposed by the feature is now enabled by default.
try-from = []
Expand Down
16 changes: 16 additions & 0 deletions src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,22 @@ macro_rules! std {
($($tt:tt)*) => {};
}

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

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

/// Expands the given block of code when `uom` is compiled with the `test` feature.
#[doc(hidden)]
#[macro_export]
Expand Down
108 changes: 107 additions & 1 deletion src/si/angle.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
//! Angle (dimensionless quantity).

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

#[cfg(feature = "use_micromath")]
use micromath::F32Ext;

quantity! {
/// Angle (dimensionless quantity).
quantity: Angle; "angle";
Expand Down Expand Up @@ -137,6 +140,49 @@ where
}
}

#[cfg(feature = "use_micromath")]
impl<D, U> crate::si::Quantity<D, U, f32>
where
D: crate::si::Dimension + ?Sized,
U: crate::si::Units<f32> + ?Sized,
{
/// Computes the four quadrant arctangent of self (y) and other (x).
#[must_use = "method returns a new number and does not mutate the original value"]
#[inline(always)]
pub fn atan2(self, other: Self) -> Angle<U, f32> {
Angle::new::<radian>(self.value.atan2(other.value))
}
}
/// Implementation of various micromath trigonometric functions
#[cfg(feature = "use_micromath")]
impl<U> Angle<U, f32>
where
U: crate::si::Units<f32> + ?Sized,
{
/// Computes the value of the cosine of the angle.
#[must_use = "method returns a new number and does not mutate the original value"]
#[inline(always)]
pub fn cos(self) -> Ratio<U, f32> {
self.value.cos().into()
}

/// Computes the value of the sine of the angle.
#[must_use = "method returns a new number and does not mutate the original value"]
#[inline(always)]
pub fn sin(self) -> Ratio<U, f32> {
self.value.sin().into()
}


/// Computes the value of the tangent of the angle.
#[must_use = "method returns a new number and does not mutate the original value"]
#[inline(always)]
pub fn tan(self) -> Ratio<U, f32> {
self.value.tan().into()
}

}

#[cfg(test)]
mod tests {
storage_types! {
Expand Down Expand Up @@ -228,4 +274,64 @@ mod tests {
}
}
}

#[cfg(feature = "use_micromath")]
mod trig {
storage_types! {
types: Float;

use crate::lib::f64::consts::PI;
use crate::num::{FromPrimitive, Zero};
use crate::si::angle as a;
use crate::si::length as l;
use crate::si::quantities::*;
use crate::tests::Test;

#[test]
fn sanity() {
let zero: Angle<V> = Angle::zero();
let nzero: Angle<V> = -Angle::zero();
let pi: Angle<V> = Angle::new::<a::radian>(V::from_f64(PI).unwrap());
let half: Angle<V> = Angle::new::<a::radian>(V::from_f64(PI / 2.0).unwrap());

Test::assert_approx_eq(&zero.cos().into(), &1.0);
Test::assert_approx_eq(&nzero.cos().into(), &1.0);

Test::assert_approx_eq(&pi.cos().into(), &-1.0);
Test::assert_approx_eq(&half.cos().into(), &0.0);

Test::assert_approx_eq(&zero.sin().into(), &0.0);
Test::assert_approx_eq(&nzero.sin().into(), &0.0);

// Float inaccuracy does not guarantee approximate values
// In these tests, it diverges slightly over the epsilon value
//Test::assert_approx_eq(&pi.sin().into(), &0.0);
//Test::assert_approx_eq(&half.sin().into(), &1.0);

Test::assert_approx_eq(&zero.tan().into(), &0.0);
Test::assert_approx_eq(&nzero.tan().into(), &0.0);

//Test::assert_approx_eq(&pi.tan(), &0.0);
// Cannot test for PI / 2 equality as it diverges to infinity
// Float inaccuracy does not guarantee a NAN or INFINITY result
//let result = half.tan().into();
//assert!(result == V::nan() || result == V::infinity());

}

quickcheck! {
#[allow(trivial_casts)]
fn atan2(y: V, x: V) -> bool {
Test::eq(&y.atan2(x),
&Length::new::<l::meter>(y).atan2(Length::new::<l::meter>(x)).get::<a::radian>())
}
}

#[test]
fn turns() {
Test::assert_approx_eq(&Angle::<V>::HALF_TURN.cos().into(), &-1.0);
Test::assert_approx_eq(&Angle::<V>::FULL_TURN.cos().into(), &1.0);
}
}
}
}
155 changes: 155 additions & 0 deletions src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,80 @@ macro_rules! system {
value: self.value.sqrt(),
}
}}

use_micromath! {


/// Raises a quantity to an integer power.
///
#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]
#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]
/// # use uom::si::f32::*;
/// # use uom::si::length::meter;
/// use uom::typenum::P2;
///
/// let a: Area = Length::new::<meter>(3.0).powi(P2::new());
/// ```
///
/// ## Generic Parameters
/// * `E`: `typenum::Integer` power.
#[must_use = "method returns a new number and does not mutate the original value"]
#[inline(always)]
pub fn powi<E>(
self, _e: E
) -> Quantity<$quantities<$($crate::typenum::Prod<D::$symbol, E>),+>, U, V>
where
$(D::$symbol: $crate::lib::ops::Mul<E>,
<D::$symbol as $crate::lib::ops::Mul<E>>::Output: $crate::typenum::Integer,)+
D::Kind: $crate::marker::Mul,
E: $crate::typenum::Integer,
{
use micromath::F32Ext;
Quantity {
dimension: $crate::lib::marker::PhantomData,
units: $crate::lib::marker::PhantomData,
value: self.value.powi(E::to_i32()),
}
}

/// Takes the square root of a number. Returns `NAN` if `self` is a negative
/// number.
///
#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]
#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]
/// # use uom::si::f32::*;
/// # use uom::si::area::square_meter;
/// let l: Length = Area::new::<square_meter>(4.0).sqrt();
/// ```
///
/// The input type must have dimensions divisible by two:
///
#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust,compile_fail")]
#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]
/// # use uom::si::f32::*;
/// # use uom::si::length::meter;
/// // error[E0271]: type mismatch resolving ...
/// let r = Length::new::<meter>(4.0).sqrt();
/// ```
#[must_use = "method returns a new number and does not mutate the original value"]
#[inline(always)]
pub fn sqrt(
self
) -> Quantity<
$quantities<$($crate::typenum::PartialQuot<D::$symbol, $crate::typenum::P2>),+>,
U, V>
where
$(D::$symbol: $crate::typenum::PartialDiv<$crate::typenum::P2>,
<D::$symbol as $crate::typenum::PartialDiv<$crate::typenum::P2>>::Output: $crate::typenum::Integer,)+
D::Kind: $crate::marker::Div,
{
use micromath::F32Ext;
Quantity {
dimension: $crate::lib::marker::PhantomData,
units: $crate::lib::marker::PhantomData,
value: self.value.sqrt(),
}
}}
}
}
}
Expand Down Expand Up @@ -1672,3 +1746,84 @@ macro_rules! system {
};
(@replace $_t:tt $sub:ty) => { $sub };
}


impl<D, U, V, E> crate::typenum::Pow<E> for crate::si::Quantity<D, U, V>
where
D: crate::si::Dimension + ?Sized,
U: crate::si::Units<V> + ?Sized,
V: crate::num::Num + crate::Conversion<V> + crate::lib::clone::Clone,
E: crate::typenum::Integer,
D::L: core::ops::Mul<E>,
D::M: core::ops::Mul<E>,
D::T: core::ops::Mul<E>,
D::I: core::ops::Mul<E>,
D::Th: core::ops::Mul<E>,
D::N: core::ops::Mul<E>,
D::J: core::ops::Mul<E>,
<D::L as core::ops::Mul<E>>::Output: crate::typenum::Integer,
<D::M as core::ops::Mul<E>>::Output: crate::typenum::Integer,
<D::T as core::ops::Mul<E>>::Output: crate::typenum::Integer,
<D::I as core::ops::Mul<E>>::Output: crate::typenum::Integer,
<D::Th as core::ops::Mul<E>>::Output: crate::typenum::Integer,
<D::N as core::ops::Mul<E>>::Output: crate::typenum::Integer,
<D::J as core::ops::Mul<E>>::Output: crate::typenum::Integer,
{
type Output = crate::si::Quantity<
crate::si::ISQ<
crate::typenum::Prod<D::L, E>,
crate::typenum::Prod<D::M, E>,
crate::typenum::Prod<D::T, E>,
crate::typenum::Prod<D::I, E>,
crate::typenum::Prod<D::Th, E>,
crate::typenum::Prod<D::N, E>,
crate::typenum::Prod<D::J, E>,
>,
U,
V,
>;

fn powi(self, exp: E) -> Self::Output {
self.powi(exp)
}
}

impl<D, U, V> crate::typenum::SquareRoot for crate::si::Quantity<D, U, V>
where
D: crate::si::Dimension + ?Sized,
U: crate::si::Units<V> + ?Sized,
V: crate::num::Num + crate::Conversion<V> + crate::lib::clone::Clone,
D::L: core::ops::Mul<crate::typenum::P2>,
D::M: core::ops::Mul<crate::typenum::P2>,
D::T: core::ops::Mul<crate::typenum::P2>,
D::I: core::ops::Mul<crate::typenum::P2>,
D::Th: core::ops::Mul<crate::typenum::P2>,
D::N: core::ops::Mul<crate::typenum::P2>,
D::J: core::ops::Mul<crate::typenum::P2>,
<D::L as core::ops::Mul<crate::typenum::P2>>::Output: crate::typenum::Integer,
<D::M as core::ops::Mul<crate::typenum::P2>>::Output: crate::typenum::Integer,
<D::T as core::ops::Mul<crate::typenum::P2>>::Output: crate::typenum::Integer,
<D::I as core::ops::Mul<crate::typenum::P2>>::Output: crate::typenum::Integer,
<D::Th as core::ops::Mul<crate::typenum::P2>>::Output: crate::typenum::Integer,
<D::N as core::ops::Mul<crate::typenum::P2>>::Output: crate::typenum::Integer,
<D::J as core::ops::Mul<crate::typenum::P2>>::Output: crate::typenum::Integer,
{
type Output = f32;
// type Output = crate::si::Quantity<
// crate::si::ISQ<
// crate::typenum::Quot<D::L, crate::typenum::P2>,
// crate::typenum::Quot<D::M, crate::typenum::P2>,
// crate::typenum::Quot<D::T, crate::typenum::P2>,
// crate::typenum::Quot<D::I, crate::typenum::P2>,
// crate::typenum::Quot<D::Th, crate::typenum::P2>,
// crate::typenum::Quot<D::N, crate::typenum::P2>,
// crate::typenum::Quot<D::J, crate::typenum::P2>,
// >,
// U,
// V,
// >;

// TODO: Figure out how to implement square root here

}