diff --git a/Cargo.lock b/Cargo.lock index b4ed9dc1..b068d6c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -142,6 +142,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "ordered-float" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c65ee1f9701bf938026630b455d5315f490640234259037edb259798b3bcf85e" +dependencies = [ + "num-traits", +] + [[package]] name = "proc-macro2" version = "1.0.88" @@ -294,6 +303,7 @@ dependencies = [ "num-complex", "num-rational", "num-traits", + "ordered-float", "quickcheck", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 66acf49c..0020c4f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,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" +ordered-float = { version = "4.5.0", optional = true, default-features = false, features = ["std"] } [dev-dependencies] approx = "0.5" @@ -67,6 +68,10 @@ rational64 = ["rational-support"] bigrational = ["bigint-support"] complex32 = ["complex-support"] complex64 = ["complex-support"] +orderedf32 = ["ordered-float-support"] +orderedf64 = ["ordered-float-support"] +notnanf32 = ["ordered-float-support"] +notnanf64 = ["ordered-float-support"] f32 = [] f64 = [] si = [] @@ -81,6 +86,7 @@ use_serde = ["serde"] rational-support = ["num-rational"] bigint-support = ["num-bigint", "num-rational/num-bigint-std"] complex-support = ["num-complex"] +ordered-float-support = ["ordered-float"] [[example]] name = "base" diff --git a/src/lib.rs b/src/lib.rs index 498810d4..f63f4bb7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -196,6 +196,8 @@ feature = "bigint", feature = "biguint", feature = "rational", feature = "rational32", feature = "rational64", feature = "bigrational", feature = "complex32", feature = "complex64", + feature = "orderedf32", feature = "orderedf64", + feature = "notnanf32", feature = "notnanf64", feature = "f32", feature = "f64", )))] compile_error!("A least one underlying storage type must be enabled. See the features section of \ uom documentation for available underlying storage type options."); @@ -215,6 +217,10 @@ pub extern crate num_rational; #[cfg(feature = "complex-support")] pub extern crate num_complex; +#[doc(hidden)] +#[cfg(feature = "ordered-float-support")] +pub extern crate ordered_float; + #[doc(hidden)] #[cfg(feature = "serde")] pub extern crate serde; @@ -224,7 +230,7 @@ pub extern crate typenum; #[cfg(all( test, - any(feature = "f32", feature = "f64", feature = "complex32", feature = "complex64") + any(feature = "f32", feature = "f64", feature = "complex32", feature = "complex64", feature = "orderedf32", feature = "orderedf64", feature="notnanf32", feature="notnanf64") ))] #[macro_use] extern crate approx; @@ -293,6 +299,15 @@ pub mod num { pub mod complex { pub use num_complex::*; } + + #[cfg(feature = "ordered-float-support")] + pub mod ordered_float { + pub use ordered_float::*; + pub type Orderedf32 = OrderedFloat; + pub type Orderedf64 = OrderedFloat; + pub type NotNanf32 = NotNan; + pub type NotNanf64 = NotNan; + } } /// Primitive traits and types representing basic properties of types. @@ -697,6 +712,83 @@ storage_types! { } } + +storage_types! { + types: OrderedFloat; + + impl crate::Conversion for V { + type T = VV; + + #[inline(always)] + fn constant(op: crate::ConstantOp) -> Self::T { + match op { + crate::ConstantOp::Add => -::zero(), + crate::ConstantOp::Sub => ::zero(), + } + } + + #[inline(always)] + fn conversion(&self) -> Self::T { + self.0 + } + } + + impl crate::ConversionFactor for VV { + #[inline(always)] + fn powi(self, e: i32) -> Self { + ::powi(self, e) + } + + #[inline(always)] + fn value(self) -> V { + ordered_float::OrderedFloat::(self) + } + } + + impl crate::ConstZero for V { + const ZERO: Self = ordered_float::OrderedFloat::(0.0); + } +} + +storage_types! { + types: NotNan; + + impl crate::Conversion for V { + type T = VV; + + #[inline(always)] + fn constant(op: crate::ConstantOp) -> Self::T { + match op { + crate::ConstantOp::Add => -::zero(), + crate::ConstantOp::Sub => ::zero(), + } + } + + #[inline(always)] + fn conversion(&self) -> Self::T { + self.into_inner() + } + } + + impl crate::ConversionFactor for VV { + #[inline(always)] + fn powi(self, e: i32) -> Self { + ::powi(self, e) + } + + #[inline(always)] + fn value(self) -> V { + ordered_float::NotNan::new(self).unwrap() + } + } + + // can't provide this since ::new().unwrap() is not const, but unsafe is forbidden. + //impl crate::ConstZero for V { + // const ZERO: Self = notnan_unwrap(ordered_float::NotNan::new(0.0)); + //} + +} + /// Utilities for formatting and printing quantities. pub mod fmt { /// An enum to specify the display style to use. diff --git a/src/storage_types.rs b/src/storage_types.rs index fb7ec2ee..0557c42d 100644 --- a/src/storage_types.rs +++ b/src/storage_types.rs @@ -115,6 +115,37 @@ macro_rules! storage_types { pub type VV = f64; $($tt)*)); }; + (@type ($(#[$attr:meta])*) @$M:ident Orderedf32 ($($tt:tt)*)) => { + storage_type_orderedf32!(($(#[$attr])*) @$M ( + /// Inner storage type. + #[allow(dead_code)] + pub type VV = f32; + $($tt)*)); + }; + (@type ($(#[$attr:meta])*) @$M:ident Orderedf64 ($($tt:tt)*)) => { + storage_type_orderedf64!(($(#[$attr])*) @$M ( + /// Inner storage type. + #[allow(dead_code)] + pub type VV = f64; + $($tt)*)); + }; + + + (@type ($(#[$attr:meta])*) @$M:ident NotNanf32 ($($tt:tt)*)) => { + storage_type_notnanf32!(($(#[$attr])*) @$M ( + /// Inner storage type. + #[allow(dead_code)] + pub type VV = f32; + $($tt)*)); + }; + (@type ($(#[$attr:meta])*) @$M:ident NotNanf64 ($($tt:tt)*)) => { + storage_type_notnanf64!(($(#[$attr])*) @$M ( + /// Inner storage type. + #[allow(dead_code)] + pub type VV = f64; + $($tt)*)); + }; + (@type ($(#[$attr:meta])*) @$M:ident f32 ($($tt:tt)*)) => { storage_type_f32!(($(#[$attr])*) @$M ($($tt)*)); }; @@ -142,6 +173,10 @@ macro_rules! storage_types { storage_types!(@type ($(#[$attr])*) @$M BigRational ($($tt)*)); storage_types!(@type ($(#[$attr])*) @$M Complex32 ($($tt)*)); storage_types!(@type ($(#[$attr])*) @$M Complex64 ($($tt)*)); + storage_types!(@type ($(#[$attr])*) @$M Orderedf32 ($($tt)*)); + storage_types!(@type ($(#[$attr])*) @$M Orderedf64 ($($tt)*)); + storage_types!(@type ($(#[$attr])*) @$M NotNanf32 ($($tt)*)); + storage_types!(@type ($(#[$attr])*) @$M NotNanf64 ($($tt)*)); storage_types!(@type ($(#[$attr])*) @$M f32 ($($tt)*)); storage_types!(@type ($(#[$attr])*) @$M f64 ($($tt)*)); }; @@ -181,6 +216,10 @@ macro_rules! storage_types { storage_types!(@type ($(#[$attr])*) @$M Rational32 ($($tt)*)); storage_types!(@type ($(#[$attr])*) @$M Rational64 ($($tt)*)); storage_types!(@type ($(#[$attr])*) @$M BigRational ($($tt)*)); + storage_types!(@type ($(#[$attr])*) @$M Orderedf32 ($($tt)*)); + storage_types!(@type ($(#[$attr])*) @$M Orderedf64 ($($tt)*)); + storage_types!(@type ($(#[$attr])*) @$M NotNanf32 ($($tt)*)); + storage_types!(@type ($(#[$attr])*) @$M NotNanf64 ($($tt)*)); storage_types!(@type ($(#[$attr])*) @$M f32 ($($tt)*)); storage_types!(@type ($(#[$attr])*) @$M f64 ($($tt)*)); }; @@ -197,6 +236,14 @@ macro_rules! storage_types { storage_types!(@type ($(#[$attr])*) @$M Complex32 ($($tt)*)); storage_types!(@type ($(#[$attr])*) @$M Complex64 ($($tt)*)); }; + (@type ($(#[$attr:meta])*) @$M:ident OrderedFloat ($($tt:tt)*)) => { + storage_types!(@type ($(#[$attr])*) @$M Orderedf32 ($($tt)*)); + storage_types!(@type ($(#[$attr])*) @$M Orderedf64 ($($tt)*)); + }; + (@type ($(#[$attr:meta])*) @$M:ident NotNan ($($tt:tt)*)) => { + storage_types!(@type ($(#[$attr])*) @$M NotNanf32 ($($tt)*)); + storage_types!(@type ($(#[$attr])*) @$M NotNanf64 ($($tt)*)); + }; (@mod ($(#[$attr:meta])*) $M:ident, $V:ty; ($($tt:tt)*)) => { $(#[$attr])* mod $M { @@ -268,6 +315,10 @@ storage_type_types! { storage_type_bigrational!("bigrational", bigrational, $crate::num::BigRational); storage_type_complex32!("complex32", complex32, $crate::num::complex::Complex32); storage_type_complex64!("complex64", complex64, $crate::num::complex::Complex64); + storage_type_orderedf32!("orderedf32", orderedf32, $crate::num::ordered_float::Orderedf32); + storage_type_orderedf64!("orderedf64", orderedf64, $crate::num::ordered_float::Orderedf64); + storage_type_notnanf32!("notnanf32", notnanf32, $crate::num::ordered_float::NotNanf32); + storage_type_notnanf64!("notnanf64", notnanf64, $crate::num::ordered_float::NotNanf64); storage_type_f32!("f32", f32, f32); storage_type_f64!("f64", f64, f64); } diff --git a/src/system.rs b/src/system.rs index 7da3cead..b0864105 100644 --- a/src/system.rs +++ b/src/system.rs @@ -761,10 +761,15 @@ macro_rules! system { // when the underlying type, `T`, implements `FloatCore`. mod float { storage_types! { - types: Float, Complex; + types: Float, OrderedFloat; + // NotNan (like Complex) does not implement Float either... + // need to figure out how to cover the rest of Quantity's contract... use super::super::*; + #[allow(unused_imports)] + use $crate::num_traits::Float; + impl Quantity where D: Dimension + ?Sized, @@ -869,14 +874,212 @@ macro_rules! system { Ua: Units + ?Sized, Ub: Units + ?Sized, { - #[allow(unused_imports)] - use $crate::num_traits::MulAdd; + //use $crate::num_traits::MulAdd; // (self * a) + b Quantity { dimension: $crate::lib::marker::PhantomData, units: $crate::lib::marker::PhantomData, value: self.value.mul_add(a.value, b.value), + //value: ::mul_add(self.value, a.value, b.value) + } + } + + /// 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::(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( + self, _e: E + ) -> Quantity<$quantities<$($crate::typenum::Prod),+>, U, V> + where + $(D::$symbol: $crate::lib::ops::Mul, + >::Output: $crate::typenum::Integer,)+ + D::Kind: $crate::marker::Mul, + E: $crate::typenum::Integer, + { + 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::(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::(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),+>, + U, V> + where + $(D::$symbol: $crate::typenum::PartialDiv<$crate::typenum::P2>, + >::Output: $crate::typenum::Integer,)+ + D::Kind: $crate::marker::Div, + { + //use $crate::num_traits::Float; + + Quantity { + dimension: $crate::lib::marker::PhantomData, + units: $crate::lib::marker::PhantomData, + value: self.value.sqrt(), + } + }} + } + } + } + + mod complex { + storage_types! { + types: Complex; + + use super::super::*; + + #[allow(unused_imports)] + use $crate::num_traits::Float; + + impl Quantity + where + D: Dimension + ?Sized, + U: Units + ?Sized, + { + /// Returns `true` if this value is `NAN` and `false` otherwise. + #[cfg_attr(clippy, allow(clippy::wrong_self_convention))] + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn is_nan(self) -> bool + { + self.value.is_nan() + } + + /// Returns `true` if this value is positive infinity or negative infinity and + /// `false` otherwise. + #[cfg_attr(clippy, allow(clippy::wrong_self_convention))] + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn is_infinite(self) -> bool + { + self.value.is_infinite() + } + + /// Returns `true` if this number is neither infinite nor `NAN`. + #[cfg_attr(clippy, allow(clippy::wrong_self_convention))] + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn is_finite(self) -> bool + { + self.value.is_finite() + } + + /// Returns `true` if the number is neither zero, infinite, subnormal, or `NAN`. + #[cfg_attr(clippy, allow(clippy::wrong_self_convention))] + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn is_normal(self) -> bool + { + self.value.is_normal() + } + + std! { + /// Takes the cubic root of a 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::volume::cubic_meter; + /// let l: Length = Volume::new::(8.0).cbrt(); + /// ``` + /// + /// The input type must have dimensions divisible by three: + /// + #[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::area::square_meter; + /// // error[E0271]: type mismatch resolving ... + /// let r = Area::new::(8.0).cbrt(); + /// ``` + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn cbrt( + self + ) -> Quantity< + $quantities<$($crate::typenum::PartialQuot),+>, + U, V> + where + $(D::$symbol: $crate::lib::ops::PartialDiv<$crate::typenum::P3>, + >::Output: $crate::typenum::Integer,)+ + D::Kind: $crate::marker::Div, + { + Quantity { + dimension: $crate::lib::marker::PhantomData, + units: $crate::lib::marker::PhantomData, + value: self.value.cbrt(), + } + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error. + /// This produces a more accurate result with better performance than a separate + /// multiplication operation followed by an add. + /// + /// ## Generic Parameters + /// * `Da`: Dimension for parameter `a`. + /// * `Ua`: Base units for parameter `a`. + /// * `Ub`: Base units for parameter `b`. + #[must_use = "method returns a new number and does not mutate the original value"] + #[inline(always)] + pub fn mul_add( + self, + a: Quantity, + b: Quantity<$quantities<$($crate::typenum::Sum),+>, Ub, V>, + ) -> Quantity<$quantities<$($crate::typenum::Sum),+>, U, V> + where + $(D::$symbol: $crate::lib::ops::Add, + >::Output: $crate::typenum::Integer,)+ + D::Kind: $crate::marker::Mul, + Da: Dimension + ?Sized, + Da::Kind: $crate::marker::Mul, + Ua: Units + ?Sized, + Ub: Units + ?Sized, + { + use $crate::num_traits::MulAdd; + + // (self * a) + b + Quantity { + dimension: $crate::lib::marker::PhantomData, + units: $crate::lib::marker::PhantomData, + //value: self.value.mul_add(a.value, b.value), + value: ::mul_add(self.value, a.value, b.value) } } @@ -942,6 +1145,8 @@ macro_rules! system { >::Output: $crate::typenum::Integer,)+ D::Kind: $crate::marker::Div, { + //use $crate::num_traits::Float; + Quantity { dimension: $crate::lib::marker::PhantomData, units: $crate::lib::marker::PhantomData, diff --git a/src/tests/asserts.rs b/src/tests/asserts.rs index 058952e6..40278c0b 100644 --- a/src/tests/asserts.rs +++ b/src/tests/asserts.rs @@ -164,3 +164,27 @@ storage_types! { assert_not_impl_any!(QuantityArguments, U, V, meter>: Binary, Eq, Hash, LowerHex, Octal, Ord, PartialEq, PartialOrd, UpperHex); } + +storage_types! { + types: OrderedFloat, NotNan; + + use super::*; + + assert_impl_all!(Quantity, U, V>: + Clone, Copy, Debug, PartialEq, PartialOrd, Send, Sync, Unpin, Eq, Ord, Hash); + #[cfg(feature = "std")] + assert_impl_all!(Quantity, U, V>: + RefUnwindSafe, UnwindSafe); + assert_not_impl_any!(Quantity, U, V>: + Binary, Display, LowerExp, LowerHex, Octal, UpperExp, UpperHex); + + + //TODO: come back here and fix this! + //assert_impl_all!(QuantityArguments, U, V, meter>: + // Clone, Copy, Debug, Display, LowerExp, Send, Sync, Unpin, UpperExp); + //#[cfg(feature = "std")] + //assert_impl_all!(QuantityArguments, U, V, meter>: + // RefUnwindSafe, UnwindSafe); + //assert_not_impl_any!(QuantityArguments, U, V, meter>: + // Binary, Eq, Hash, LowerHex, Octal, Ord, PartialEq, PartialOrd, UpperHex); +} \ No newline at end of file diff --git a/src/tests/mod.rs b/src/tests/mod.rs index c6e705f0..ccb21f8f 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -196,6 +196,91 @@ mod test_trait { } } } + + + storage_types! { + types: OrderedFloat; + + use crate::num::Float; + + // const EPSILON: VV = 64.0 * VV::epsilon(); //error[E0015]; calls in constants are limited... + const EPS_FACTOR: VV = 0.5; + const ULPS: u32 = 3; + + impl super::super::Test for V { + /// Assert that `lhs` and `rhs` are exactly equal. + fn assert_eq(lhs: &Self, rhs: &Self) { + match (lhs.is_nan(), rhs.is_nan()) { + (true, true) => {} + _ => { assert_eq!(lhs, rhs); } + } + } + + /// Assert that `lhs` and `rhs` are approximately equal for floating point types or + /// exactly equal for other types. + fn assert_approx_eq(lhs: &Self, rhs: &Self) { + match (lhs.is_nan(), rhs.is_nan()) { + (true, true) => {} + _ => { + assert_ulps_eq!(lhs.0, rhs.0, epsilon = EPS_FACTOR * VV::epsilon(), max_ulps = ULPS); + } + } + } + + /// Exactly compare `lhs` and `rhs` and return the result. + fn eq(lhs: &Self, rhs: &Self) -> bool { + (lhs.is_nan() && rhs.is_nan()) || lhs == rhs + } + + /// Approximately compare `lhs` and `rhs` for floating point types or exactly compare + /// for other types and return the result. + fn approx_eq(lhs: &Self, rhs: &Self) -> bool { + (lhs.is_nan() && rhs.is_nan()) || ulps_eq!(lhs.0, rhs.0, epsilon = EPS_FACTOR * VV::epsilon(), max_ulps = ULPS) + } + } + } + + storage_types! { + types: NotNan; + + use crate::num::Float; + + // const EPSILON: VV = 64.0 * VV::epsilon(); //error[E0015]; calls in constants are limited... + const EPS_FACTOR: VV = 0.5; + const ULPS: u32 = 3; + + impl super::super::Test for V { + /// Assert that `lhs` and `rhs` are exactly equal. + fn assert_eq(lhs: &Self, rhs: &Self) { + match (lhs.is_nan(), rhs.is_nan()) { + (true, true) => {} + _ => { assert_eq!(lhs, rhs); } + } + } + + /// Assert that `lhs` and `rhs` are approximately equal for floating point types or + /// exactly equal for other types. + fn assert_approx_eq(lhs: &Self, rhs: &Self) { + match (lhs.is_nan(), rhs.is_nan()) { + (true, true) => {} + _ => { + assert_ulps_eq!(lhs.as_ref(), rhs.as_ref(), epsilon = EPS_FACTOR * VV::epsilon(), max_ulps = ULPS); + } + } + } + + /// Exactly compare `lhs` and `rhs` and return the result. + fn eq(lhs: &Self, rhs: &Self) -> bool { + (lhs.is_nan() && rhs.is_nan()) || lhs == rhs + } + + /// Approximately compare `lhs` and `rhs` for floating point types or exactly compare + /// for other types and return the result. + fn approx_eq(lhs: &Self, rhs: &Self) -> bool { + (lhs.is_nan() && rhs.is_nan()) || ulps_eq!(lhs.as_ref(), rhs.as_ref(), epsilon = EPS_FACTOR * VV::epsilon(), max_ulps = ULPS) + } + } + } } #[derive(Clone, Debug)] @@ -260,6 +345,41 @@ mod a_struct { } } } + + storage_types! { + types: OrderedFloat; + + use super::super::A; + + impl quickcheck::Arbitrary for A { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + A { + v: ordered_float::OrderedFloat(::arbitrary(g)), + } + } + } + } + + storage_types! { + types: NotNan; + + use super::super::A; + + impl quickcheck::Arbitrary for A { + fn arbitrary(g: &mut quickcheck::Gen) -> Self { + let mut fp = VV::NAN; + while fp.is_nan() || fp < 1.0 || fp > 1e19{ + // this is a dirty hack that only allows positive values + // between 1.0 and f32::Max / 2 so that arithmetic can't + // result in a NaN. Possibly, NotNan could be excluded + // from the catch all tests and handled specially. + fp = ::arbitrary(g); + } + + A { v: ordered_float::NotNan::new(fp).unwrap() } + } + } + } } mod asserts; diff --git a/src/tests/quantity.rs b/src/tests/quantity.rs index 22068572..ea764001 100644 --- a/src/tests/quantity.rs +++ b/src/tests/quantity.rs @@ -285,7 +285,7 @@ mod fmt { #[cfg(feature = "autoconvert")] mod non_big { storage_types! { - types: Float, PrimInt, Rational, Rational32, Rational64; + types: Float, PrimInt, Rational, Rational32, Rational64, OrderedFloat; use crate::tests::*; @@ -496,7 +496,7 @@ mod float { mod non_complex { storage_types! { // Everything BUT complex - types: PrimInt, Ratio, Float, BigInt, BigUint; + types: PrimInt, Ratio, Float, BigInt, BigUint, OrderedFloat, NotNan; use crate::tests::*; diff --git a/src/tests/system.rs b/src/tests/system.rs index 99e48491..5e88b6f6 100644 --- a/src/tests/system.rs +++ b/src/tests/system.rs @@ -239,7 +239,7 @@ mod prim_int { mod float { storage_types! { - types: Float; + types: Float, OrderedFloat; use crate::tests::*; @@ -362,14 +362,21 @@ mod float { #[allow(trivial_casts)] fn max(l: A, r: A) -> bool { - Test::eq(&Length::new::(l.max(*r)), + let lr_max = num_traits::float::FloatCore::max(*l, *r); + Test::eq( + &Length::new::(lr_max), + //&Length::new::(l.max(*r)), &Length::new::(*l).max(Length::new::(*r))) } #[allow(trivial_casts)] fn min(l: A, r: A) -> bool { - Test::eq(&Length::new::(l.min(*r)), - &Length::new::(*l).min(Length::new::(*r))) + let lr_min = num_traits::float::FloatCore::min(*l, *r); + Test::eq( + //&Length::new::(l.min(*r)), + &Length::new::(lr_min), + &Length::new::(*l).min(Length::new::(*r)) + ) } } } @@ -407,7 +414,7 @@ mod signed { mod non_ratio { storage_types! { - types: PrimInt, BigInt, BigUint, Float; + types: PrimInt, BigInt, BigUint, Float, OrderedFloat, NotNan; use crate::tests::*; @@ -422,7 +429,7 @@ mod non_ratio { mod non_big { storage_types! { - types: PrimInt, Rational, Rational32, Rational64, Float; + types: PrimInt, Rational, Rational32, Rational64, Float, OrderedFloat; use crate::tests::*; @@ -697,7 +704,7 @@ mod complex { mod non_complex { storage_types! { - types: PrimInt, Float, Ratio, BigInt, BigUint; + types: PrimInt, Float, Ratio, BigInt, BigUint, OrderedFloat, NotNan; use crate::tests::*; diff --git a/src/unit.rs b/src/unit.rs index 0ef9cd9f..f0b8d16e 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -363,7 +363,43 @@ macro_rules! unit { } })+ } + + storage_types! { + types: OrderedFloat, NotNan; + + $(impl $crate::Conversion for super::$unit { + type T = VV; + + #[inline(always)] + #[allow(clippy::inconsistent_digit_grouping)] + fn coefficient() -> Self::T { + unit!(@coefficient $($conversion),+) + } + + #[inline(always)] + #[allow(unused_variables)] + #[allow(clippy::inconsistent_digit_grouping)] + fn constant(op: $crate::ConstantOp) -> Self::T { + unit!(@constant op $($conversion),+) + } + } + + impl super::Conversion for super::$unit { + #[cfg(test)] + #[inline(always)] + fn is_valid() -> bool { + use $crate::num::ToPrimitive; + + let r = Some(unit!(@coefficient $($conversion),+)); + let c = >::coefficient().to_f64(); + + r == c + } + })+ + } + }; + (@unit $(#[$unit_attr:meta])+ @$unit:ident $plural:expr) => { $(#[$unit_attr])* #[allow(non_camel_case_types)] diff --git a/tests/temp.rs b/tests/temp.rs new file mode 100644 index 00000000..0e5a0704 --- /dev/null +++ b/tests/temp.rs @@ -0,0 +1,50 @@ +#[cfg(feature = "notnanf32")] +mod notnan { + use uom::si::notnanf32::*; + use uom::num::ordered_float::NotNan; + use uom::si::length::{kilometer, meter}; + + #[test] + fn test_not_nan32() { + let x = Length::new::(NotNan(5.0)); + + x.is_nan(); + x.is_finite(); + x.is_infinite(); + //x.sqrt(); + //x.cbrt(); + } + +} + +#[cfg(feature = "orderedf32")] +mod order32 { +use uom::si::orderedf32::*; +use uom::num::ordered_float::OrderedFloat; +use uom::si::length::{kilometer, meter}; + +#[test] +fn test_of32() { + let x = Length::new::(OrderedFloat(5.0)); + assert_eq!(x.get::(), OrderedFloat(5000.0)); + + let x2 = x * x; + let x3 = x * x * x; + + x.is_nan(); + x.is_finite(); + x.is_infinite(); + //x2.sqrt(); + //x3.cbrt(); +} + +#[test] +fn foo() { + let x = &30.0 % & 10.0; + + let a = ordered_float::OrderedFloat(30.0); + let b = ordered_float::OrderedFloat(10.0); + let xx = &a % &b; + +} +} \ No newline at end of file