From 9cda2d61c4c09f72ace77f3ecc1f6a152f2b3e40 Mon Sep 17 00:00:00 2001 From: Gustav Palmqvist Date: Sat, 12 Dec 2020 01:08:42 +0100 Subject: [PATCH 1/2] Added logarithmic units --- src/lib.rs | 44 +++++++++++++++++++++++++++++- src/quantity.rs | 4 +-- src/si/electric_potential.rs | 5 ++++ src/si/power.rs | 16 +++++++++++ src/si/ratio.rs | 2 ++ src/unit.rs | 52 ++++++++++++++++++++++++++++++++++++ 6 files changed, 120 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c095a00d..7270413c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -421,7 +421,8 @@ pub enum ConstantOp { /// /// [units]: https://jcgm.bipm.org/vim/en/1.13.html /// [factor]: https://jcgm.bipm.org/vim/en/1.24.html -pub trait Conversion { +pub trait Conversion where + V: Conversion { /// Conversion factor type specific to the underlying storage type. type T: ConversionFactor; @@ -447,6 +448,28 @@ pub trait Conversion { ::zero() } + #[inline(always)] + #[allow(unused_variables)] + fn base() -> Self::T { + ::one() + } + + #[inline(always)] + #[allow(unused_variables)] + fn scale() -> Self::T { + ::one() + } + + #[inline(always)] + fn into_linear(x: V) -> V { + x + } + + #[inline(always)] + fn from_linear(x: V) -> V { + x + } + /// Instance [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html). /// /// Default implementation returns the coefficient: `Self::coefficient()`. @@ -480,6 +503,16 @@ pub trait ConversionFactor: #[must_use = "method returns a new number and does not mutate the original value"] fn powi(self, e: i32) -> Self; + /// Raises a `ConversionFactor` to a power. + fn pow(self, v: V) -> V { + unimplemented!() + } + + /// Takes the log_`ConversionFactor` of a value. + fn log(self, v: V) -> V { + unimplemented!() + } + /// Converts a `ConversionFactor` into its underlying storage type. #[must_use = "method returns a new number and does not mutate the original value"] fn value(self) -> V; @@ -549,6 +582,15 @@ storage_types! { fn value(self) -> V { self } + + fn pow(self, v: V) -> V { + ::powf(self, v) + } + + /// Takes the log_`ConversionFactor` of a value. + fn log(self, v: V) -> V { + ::log(self, v) + } } impl crate::ConstZero for V { diff --git a/src/quantity.rs b/src/quantity.rs index e9ba92a0..1543e952 100644 --- a/src/quantity.rs +++ b/src/quantity.rs @@ -227,7 +227,7 @@ macro_rules! quantity { $quantity { dimension: $crate::lib::marker::PhantomData, units: $crate::lib::marker::PhantomData, - value: __system::to_base::(&v), + value: __system::to_base::(&::into_linear(v)), } } @@ -241,7 +241,7 @@ macro_rules! quantity { where N: Unit + $crate::Conversion, { - __system::from_base::(&self.value) + ::from_linear(__system::from_base::(&self.value)) } /// Returns the largest integer less than or equal to a number in the given diff --git a/src/si/electric_potential.rs b/src/si/electric_potential.rs index 0bce3ccb..f5f7de0a 100644 --- a/src/si/electric_potential.rs +++ b/src/si/electric_potential.rs @@ -38,6 +38,11 @@ quantity! { @abvolt: 1.0_E-8; "abV", "abvolt", "abvolts"; @statvolt: 2.997_925_E2; "statV", "statvolt", "statvolts"; + + @decibel_volt: prefix!(none), 10.0, 20.0; "dBV", "decibel-volt", "decibel-volts"; + @decibel_millivolt: prefix!(milli), 10.0, 20.0; "dBmV", "decibel-millivolt", "decibel-millivolts"; + @decibel_microvolt: prefix!(micro), 10.0, 20.0; "dBµV", "decibel-microvolt", "decibel-microvolts"; + @decibel_unit: 0.7746E0, 10.0, 20.0; "dBu", "decibel-unit", "decibel-units"; } } diff --git a/src/si/power.rs b/src/si/power.rs index 844c74a8..fb3cfa0f 100644 --- a/src/si/power.rs +++ b/src/si/power.rs @@ -36,6 +36,10 @@ quantity! { @zeptowatt: prefix!(zepto); "zW", "zeptowatt", "zeptowatts"; @yoctowatt: prefix!(yocto); "yW", "yoctowatt", "yoctowatts"; + @decibel_watt: prefix!(none), 10.0, 10.0; "dBW", "decibel-watt", "decibel-watts"; + @decibel_milliwatt: prefix!(milli), 10.0, 10.0; "dBm", "decibel-milliwatt", "decibel-milliwatts";// dBm is more common than dBmW + @decibel_microwatt: prefix!(micro), 10.0, 10.0; "dBµW", "decibel-microwatt", "decibel-microwatts"; + @erg_per_second: 1.0_E-7; "erg/s", "erg per second", "ergs per second"; @foot_pound_per_hour: 3.766_161_111_111_111_E-4; "ft · lbf/h", "foot pound-force per hour", "foot pounds-force per hour"; @@ -58,6 +62,18 @@ quantity! { #[cfg(test)] mod tests { + + #[test] + fn test_dbm() { + use crate::si::power as p; + use crate::si::quantities::*; + use crate::tests::Test; + + let x = Power::new::(0.0); + println!("{:?}", x.get::()); + println!("{:?}", x.get::()); + } + storage_types! { use crate::num::One; use crate::si::energy as e; diff --git a/src/si/ratio.rs b/src/si/ratio.rs index bd789871..b8db559a 100644 --- a/src/si/ratio.rs +++ b/src/si/ratio.rs @@ -29,6 +29,8 @@ quantity! { @part_per_billion: 1.0_E-9; "ppb", "part per billion", "parts per billion"; @part_per_trillion: 1.0_E-12; "ppt", "part per trillion", "parts per trillion"; @part_per_quadrillion: 1.0_E-15; "ppq", "part per quadrillion", "parts per quadrillion"; + + @decibel: 1.0, 10.0, 20.0; "dB", "decibel", "decibels"; } } diff --git a/src/unit.rs b/src/unit.rs index cf86fc7b..1fef3e49 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -158,6 +158,20 @@ macro_rules! unit { fn constant(op: $crate::ConstantOp) -> Self::T { unit!(@constant op $($conversion),+) } + + #[inline(always)] + #[allow(unused_variables)] + fn base() -> Self::T { + unit!(@base $($conversion),+) + } + + #[inline(always)] + #[allow(unused_variables)] + fn scale() -> Self::T { + unit!(@scale $($conversion),+) + } + + unit!(@logarithmic $($conversion),+); } impl super::Conversion for super::$unit {})+ @@ -284,12 +298,50 @@ macro_rules! unit { pub struct $unit; }; (@coefficient $factor:expr, $const:expr) => { $factor }; + (@coefficient $factor:expr, $base:expr, $scale:expr) => { $factor }; (@coefficient $factor:expr) => { $factor }; (@constant $op:ident $factor:expr, $const:expr) => { $const }; + (@constant $op:ident $factor:expr, $base:expr, $scale:expr) => { + match $op { + $crate::ConstantOp::Add => -0.0, + $crate::ConstantOp::Sub => 0.0, + } + }; (@constant $op:ident $factor:expr) => { match $op { $crate::ConstantOp::Add => -0.0, $crate::ConstantOp::Sub => 0.0, } }; + (@base $factor:expr, $base:expr, $scale:expr) => { $base }; + (@base $factor:expr, $const:expr) => { 1.0 }; + (@base $factor:expr) => { 1.0 }; + (@scale $factor:expr, $base:expr, $scale:expr) => { $scale }; + (@scale $factor:expr, $const:expr) => { 1.0 }; + (@scale $factor:expr) => { 1.0 }; + (@logarithmic $factor:expr, $base:expr, $scale:expr) => { + #[inline(always)] + fn into_linear(x: V) -> V { + use $crate::ConversionFactor; + + >::base().pow( + ((<>::T as $crate::num::One>::one() + / >::scale()) + * x.into_conversion()) + .value()) + } + + #[inline(always)] + fn from_linear(x: V) -> V { + use $crate::ConversionFactor; + + (>::scale() + * x.into_conversion() + .log(>::base().value()) + .into_conversion()) + .value() + } + }; + (@logarithmic $factor:expr, $const:expr) => { }; + (@logarithmic $factor:expr) => { }; } From d93e17bcbc8a704234833a96a296a67c9c79c5d4 Mon Sep 17 00:00:00 2001 From: Mike Boutin Date: Thu, 4 Feb 2021 20:24:43 -0500 Subject: [PATCH 2/2] TODO: fixme! Logarithmic units require `pow`/`root` functions which are not available in no-std/no-libm builds. Excluding logarthmic units has proven difficult (in-place filter foiled by macros unable to be used inside an enum definition). Using a tt-muncher could possibly solve the issue, but would likely hit the recursion limit very quickly and likely the quadratic performance characteristics would make `uom`'s slow compilation even slower. --- src/lib.rs | 124 ++++++++++++++++++------- src/quantity.rs | 20 ++-- src/si/electric_potential.rs | 8 +- src/si/power.rs | 18 +--- src/si/ratio.rs | 8 +- src/system.rs | 61 +++++++++--- src/unit.rs | 175 ++++++++++++++++++----------------- 7 files changed, 256 insertions(+), 158 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7270413c..abab873f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -421,8 +421,7 @@ pub enum ConstantOp { /// /// [units]: https://jcgm.bipm.org/vim/en/1.13.html /// [factor]: https://jcgm.bipm.org/vim/en/1.24.html -pub trait Conversion where - V: Conversion { +pub trait Conversion { /// Conversion factor type specific to the underlying storage type. type T: ConversionFactor; @@ -448,26 +447,26 @@ pub trait Conversion where ::zero() } + /// Base portion of [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html) for converting + /// the given logarithmic unit to the base unit for the quantity: `base().pow((value * + /// coefficient() + constant()) / scale())`. Implementation should return zero + /// (`Self::T::zero()`) if no base exists. + #[cfg(any(feature = "std", feature = "libm"))] + #[must_use = "method returns a new number and does not mutate the original value"] #[inline(always)] - #[allow(unused_variables)] fn base() -> Self::T { - ::one() + ::zero() } + /// Scale portion of [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html) for converting + /// the given logarithmic unit to the base unit for the quantity: `base().pow((value * + /// coefficient() + constant()) / scale())`. Implementation should return zero + /// (`Self::T::zero()`) if no base exists. + #[cfg(any(feature = "std", feature = "libm"))] + #[must_use = "method returns a new number and does not mutate the original value"] #[inline(always)] - #[allow(unused_variables)] fn scale() -> Self::T { - ::one() - } - - #[inline(always)] - fn into_linear(x: V) -> V { - x - } - - #[inline(always)] - fn from_linear(x: V) -> V { - x + ::zero() } /// Instance [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html). @@ -504,14 +503,14 @@ pub trait ConversionFactor: fn powi(self, e: i32) -> Self; /// Raises a `ConversionFactor` to a power. - fn pow(self, v: V) -> V { - unimplemented!() - } + #[cfg(any(feature = "std", feature = "libm"))] + #[must_use = "method returns a new number and does not mutate the original value"] + fn pow(self, e: Self) -> Self; /// Takes the log_`ConversionFactor` of a value. - fn log(self, v: V) -> V { - unimplemented!() - } + #[cfg(any(feature = "std", feature = "libm"))] + #[must_use = "method returns a new number and does not mutate the original value"] + fn log(self, base: Self) -> Self; /// Converts a `ConversionFactor` into its underlying storage type. #[must_use = "method returns a new number and does not mutate the original value"] @@ -578,18 +577,21 @@ storage_types! { ::powi(self, e) } + #[cfg(any(feature = "std", feature = "libm"))] #[inline(always)] - fn value(self) -> V { - self + fn pow(self, e: Self) -> Self { + ::powf(self, e) } - fn pow(self, v: V) -> V { - ::powf(self, v) + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + fn log(self, base: Self) -> Self { + ::log(self, base) } - /// Takes the log_`ConversionFactor` of a value. - fn log(self, v: V) -> V { - ::log(self, v) + #[inline(always)] + fn value(self) -> V { + self } } @@ -613,7 +615,19 @@ storage_types! { impl crate::ConversionFactor for crate::num::rational::Ratio { #[inline(always)] fn powi(self, e: i32) -> Self { - self.pow(e) + crate::num::rational::Ratio::::pow(&self, e) + } + + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + fn pow(self, _e: Self) -> Self { + unimplemented!(); + } + + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + fn log(self, _base: Self) -> Self { + unimplemented!(); } #[inline(always)] @@ -649,6 +663,18 @@ storage_types! { } } + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + fn pow(self, e: Self) -> Self { + crate::num::rational::Ratio::::pow(&self, e) + } + + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + fn log(self, base: Self) -> Self { + crate::num::rational::Ratio::::log(&self, base) + } + #[inline(always)] fn value(self) -> V { self.to_integer() @@ -671,7 +697,19 @@ storage_types! { impl crate::ConversionFactor for V { #[inline(always)] fn powi(self, e: i32) -> Self { - self.pow(e) + V::pow(&self, e) + } + + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + fn pow(self, _e: Self) -> Self { + unimplemented!(); + } + + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + fn log(self, _base: Self) -> Self { + unimplemented!(); } #[inline(always)] @@ -703,6 +741,18 @@ storage_types! { } } + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + fn pow(self, _e: Self) -> Self { + unimplemented!(); + } + + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + fn log(self, _base: Self) -> Self { + unimplemented!(); + } + #[inline(always)] fn value(self) -> V { self @@ -737,6 +787,18 @@ storage_types! { self.powi(e) } + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + fn pow(self, e: Self) -> Self { + self.powf(e) + } + + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + fn log(self, base: Self) -> Self { + VV::log(self, base) + } + #[inline(always)] fn value(self) -> V { // Conversion by scaling (multiplication with only real number). Scaling a normalized diff --git a/src/quantity.rs b/src/quantity.rs index 1543e952..1a0ce968 100644 --- a/src/quantity.rs +++ b/src/quantity.rs @@ -18,8 +18,8 @@ /// * `$conversion`: Conversion (coefficient and constant factor) from the unit to the base unit of /// the quantity (e.g. `3.048_E-1` to convert `foot` to `meter`. `1.0_E0, 273.15_E0` to convert /// `celsius` to `kelvin`.). The coefficient is required and the constant factor is optional. -/// Note that using a unit with a non-zero constant factor is not currently supported as a base -/// unit. +/// Note that using a unit with a non-zero constant factor, base, or scale is not currently +/// supported as a base unit. /// * `$abbreviation`: Unit abbreviation (e.g. `"m"`). /// * `$singular`: Singular unit description (e.g. `"meter"`). /// * `$plural`: Plural unit description (e.g. `"meters"`). @@ -157,8 +157,11 @@ macro_rules! quantity { #[allow(clippy::manual_non_exhaustive)] #[derive(Debug, Clone, Copy)] pub enum Units { - $(#[doc=$plural] - $unit($unit),)+ + $(unit! { + @supported_attr $($conversion),+; + #[doc=$plural] + $unit($unit), + })+ #[doc(hidden)] __nonexhaustive, @@ -170,7 +173,10 @@ macro_rules! quantity { #[allow(dead_code)] pub fn abbreviation(&self) -> &'static str { match self { - $(Units::$unit(_) => <$unit as __system::Unit>::abbreviation(),)+ + $(unit! { + @supported_attr $($conversion),+; + Units::$unit(_) => <$unit as __system::Unit>::abbreviation(), + })+ Units::__nonexhaustive => "unknown", } @@ -227,7 +233,7 @@ macro_rules! quantity { $quantity { dimension: $crate::lib::marker::PhantomData, units: $crate::lib::marker::PhantomData, - value: __system::to_base::(&::into_linear(v)), + value: __system::to_base::(&v), } } @@ -241,7 +247,7 @@ macro_rules! quantity { where N: Unit + $crate::Conversion, { - ::from_linear(__system::from_base::(&self.value)) + __system::from_base::(&self.value) } /// Returns the largest integer less than or equal to a number in the given diff --git a/src/si/electric_potential.rs b/src/si/electric_potential.rs index f5f7de0a..fd8f5566 100644 --- a/src/si/electric_potential.rs +++ b/src/si/electric_potential.rs @@ -40,9 +40,11 @@ quantity! { @statvolt: 2.997_925_E2; "statV", "statvolt", "statvolts"; @decibel_volt: prefix!(none), 10.0, 20.0; "dBV", "decibel-volt", "decibel-volts"; - @decibel_millivolt: prefix!(milli), 10.0, 20.0; "dBmV", "decibel-millivolt", "decibel-millivolts"; - @decibel_microvolt: prefix!(micro), 10.0, 20.0; "dBµV", "decibel-microvolt", "decibel-microvolts"; - @decibel_unit: 0.7746E0, 10.0, 20.0; "dBu", "decibel-unit", "decibel-units"; + @decibel_millivolt: prefix!(milli), 10.0, 20.0; "dBmV", "decibel-millivolt", + "decibel-millivolts"; + @decibel_microvolt: prefix!(micro), 10.0, 20.0; "dBµV", "decibel-microvolt", + "decibel-microvolts"; + @decibel_unit: 7.746_E-1, 10.0, 20.0; "dBu", "decibel-unit", "decibel-units"; } } diff --git a/src/si/power.rs b/src/si/power.rs index fb3cfa0f..c83d3d8c 100644 --- a/src/si/power.rs +++ b/src/si/power.rs @@ -37,8 +37,10 @@ quantity! { @yoctowatt: prefix!(yocto); "yW", "yoctowatt", "yoctowatts"; @decibel_watt: prefix!(none), 10.0, 10.0; "dBW", "decibel-watt", "decibel-watts"; - @decibel_milliwatt: prefix!(milli), 10.0, 10.0; "dBm", "decibel-milliwatt", "decibel-milliwatts";// dBm is more common than dBmW - @decibel_microwatt: prefix!(micro), 10.0, 10.0; "dBµW", "decibel-microwatt", "decibel-microwatts"; + @decibel_milliwatt: prefix!(milli), 10.0, 10.0; "dBmW", "decibel-milliwatt", + "decibel-milliwatts"; + @decibel_microwatt: prefix!(micro), 10.0, 10.0; "dBµW", "decibel-microwatt", + "decibel-microwatts"; @erg_per_second: 1.0_E-7; "erg/s", "erg per second", "ergs per second"; @foot_pound_per_hour: 3.766_161_111_111_111_E-4; "ft · lbf/h", "foot pound-force per hour", @@ -62,18 +64,6 @@ quantity! { #[cfg(test)] mod tests { - - #[test] - fn test_dbm() { - use crate::si::power as p; - use crate::si::quantities::*; - use crate::tests::Test; - - let x = Power::new::(0.0); - println!("{:?}", x.get::()); - println!("{:?}", x.get::()); - } - storage_types! { use crate::num::One; use crate::si::energy as e; diff --git a/src/si/ratio.rs b/src/si/ratio.rs index b8db559a..26b44106 100644 --- a/src/si/ratio.rs +++ b/src/si/ratio.rs @@ -30,7 +30,7 @@ quantity! { @part_per_trillion: 1.0_E-12; "ppt", "part per trillion", "parts per trillion"; @part_per_quadrillion: 1.0_E-15; "ppq", "part per quadrillion", "parts per quadrillion"; - @decibel: 1.0, 10.0, 20.0; "dB", "decibel", "decibels"; + @decibel: 1.0, 10.0, 10.0; "dB", "decibel", "decibels"; } } @@ -182,7 +182,7 @@ mod convert { #[cfg(test)] mod tests { storage_types! { - use crate::num::{FromPrimitive, One}; + use crate::num::{FromPrimitive, One, Zero}; use crate::si::quantities::*; use crate::si::ratio as r; use crate::tests::Test; @@ -219,6 +219,10 @@ mod tests { Test::assert_eq(&Ratio::new::(V::one() / V::from_f64(1.0_E15).unwrap()), &Ratio::new::(V::one())); + Test::assert_eq(&Ratio::new::(V::one()), + &Ratio::new::(V::zero())); + Test::assert_eq(&Ratio::new::(V::from_u8(10).unwrap()), + &Ratio::new::(V::from_u8(10).unwrap())); } } diff --git a/src/system.rs b/src/system.rs index f8f4e37c..fe83908c 100644 --- a/src/system.rs +++ b/src/system.rs @@ -317,12 +317,28 @@ macro_rules! system { let f = V::coefficient() $(* U::$name::coefficient().powi(D::$symbol::to_i32()))+; let n_cons = N::constant($crate::ConstantOp::Sub); - if n_coef < f { - (v * (f / n_coef) - n_cons).value() - } - else { - (v / (n_coef / f) - n_cons).value() - } + let result = if n_coef < f { + v * (f / n_coef) - n_cons + } + else { + v / (n_coef / f) - n_cons + }; + #[cfg(any(feature = "std", feature = "libm"))] + let result = { + use $crate::num::Zero; + + let scale = N::scale(); + let base = N::base(); + + if scale != V::T::zero() && base != V::T::zero() { + scale * result.log(base) + } + else { + result + } + }; + + result.value() } /// Convert a value from the given unit to base units. @@ -348,12 +364,29 @@ macro_rules! system { let f = V::coefficient() $(* U::$name::coefficient().powi(D::$symbol::to_i32()))+; let n_cons = N::constant($crate::ConstantOp::Add); - if n_coef >= f { - ((v + n_cons) * (n_coef / f)).value() - } - else { - (((v + n_cons) * n_coef) / f).value() - } + let result = + if n_coef >= f { + (v + n_cons) * (n_coef / f) + } + else { + ((v + n_cons) * n_coef) / f + }; + #[cfg(any(feature = "std", feature = "libm"))] + let result = { + use $crate::num::Zero; + + let scale = N::scale(); + let base = N::base(); + + if scale != V::T::zero() && base != V::T::zero() { + base.pow(result / scale) + } + else { + result + } + }; + + result.value() } autoconvert_test! { @@ -1548,8 +1581,8 @@ macro_rules! system { /// * `$V`: Underlying value storage type (e.g. `f32`). /// * `$U`: Optional. Base units. Pass as a tuple with the desired units: `(meter, kilogram, /// second, ampere, kelvin, mole, candela)`. The system's base units will be used if no - /// value is provided. Note that a unit with a non-zero constant factor is not currently - /// supported as a base unit. + /// value is provided. Note that a unit with a non-zero constant factor, base, or scale + /// is not currently supported as a base unit. /// /// An example invocation is given below for a meter-kilogram-second system setup in the /// module `mks` with a system of quantities name `Q`. The `#[macro_use]` attribute must be diff --git a/src/unit.rs b/src/unit.rs index 1fef3e49..a43ad806 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -12,11 +12,12 @@ /// * `quantity`: Path to the module where the [`quantity!`](macro.quantity.html) macro was run /// (e.g. `uom::si::length`). /// * `$unit`: Unit name (e.g. `meter`, `foot`). -/// * `$conversion`: Conversion (coefficient and constant factor) from the unit to the base unit of -/// the quantity (e.g. `3.048_E-1` to convert `foot` to `meter`. `1.0_E0, 273.15_E0` to convert -/// `celsius` to `kelvin`.). The coefficient is required and the constant factor is optional. -/// Note that using a unit with a non-zero constant factor is not currently supported as a base -/// unit. +/// * `$conversion`: Conversion (coefficient; coefficient and constant; or coefficient, base, and +/// scale) from the unit to the base unit of the quantity (e.g. `3.048_E-1` to convert `foot` to +/// `meter`. `1.0_E0, 273.15_E0` to convert `celsius` to `kelvin`. `1.0_E0, 10.0_E0, 20.0_E0` to +/// convert `decibel-volt` to `volt`). The coefficient is always required. The constant factor or +/// base and scale are optional. Note that using a unit with a non-zero constant factor, base, or +/// scale is not currently supported as a base unit. /// * `$abbreviation`: Unit abbreviation (e.g. `"m"`). /// * `$singular`: Singular unit description (e.g. `"meter"`). /// * `$plural`: Plural unit description (e.g. `"meters"`). @@ -119,62 +120,69 @@ macro_rules! unit { @units $($(#[$unit_attr:meta])* @$unit:ident: $($conversion:expr),+; $abbreviation:expr, $singular:expr, $plural:expr;)+ ) => { - $(unit!(@unit $(#[$unit_attr])* @$unit $plural); - - impl __system::Unit for $unit { - #[inline(always)] - fn abbreviation() -> &'static str { - $abbreviation - } - - #[inline(always)] - fn singular() -> &'static str { - $singular - } - - #[inline(always)] - fn plural() -> &'static str { - $plural - } - } - - impl Unit for $unit {})+ - - storage_types! { - types: Float; - - $(impl $crate::Conversion for super::$unit { - type T = V; + $(//#[cfg(any())] + unit! { + @supported $($conversion),+; + unit!(@unit $(#[$unit_attr])* @$unit $plural); + impl __system::Unit for $unit { #[inline(always)] - #[allow(clippy::inconsistent_digit_grouping)] - fn coefficient() -> Self::T { - unit!(@coefficient $($conversion),+) + fn abbreviation() -> &'static str { + $abbreviation } #[inline(always)] - #[allow(unused_variables)] - #[allow(clippy::inconsistent_digit_grouping)] - fn constant(op: $crate::ConstantOp) -> Self::T { - unit!(@constant op $($conversion),+) + fn singular() -> &'static str { + $singular } #[inline(always)] - #[allow(unused_variables)] - fn base() -> Self::T { - unit!(@base $($conversion),+) + fn plural() -> &'static str { + $plural } + } - #[inline(always)] - #[allow(unused_variables)] - fn scale() -> Self::T { - unit!(@scale $($conversion),+) - } + impl Unit for $unit {} + })+ - unit!(@logarithmic $($conversion),+); - } + storage_types! { + types: Float; - impl super::Conversion for super::$unit {})+ + $(unit! { + @supported $($conversion),+; + impl $crate::Conversion for super::$unit { + type T = V; + + #[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),+) + } + + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + #[allow(clippy::inconsistent_digit_grouping)] + fn base() -> Self::T { + unit!(@base $($conversion),+) + } + + #[cfg(any(feature = "std", feature = "libm"))] + #[inline(always)] + #[allow(clippy::inconsistent_digit_grouping)] + fn scale() -> Self::T { + unit!(@scale $($conversion),+) + } + } + + impl super::Conversion for super::$unit {} + })+ } storage_types! { @@ -186,7 +194,8 @@ macro_rules! unit { ::from_f64(value).unwrap() } - $(impl $crate::Conversion for super::$unit { + $(unit!(@supported $($conversion),+); + impl $crate::Conversion for super::$unit { type T = T; #[inline(always)] @@ -201,6 +210,7 @@ macro_rules! unit { } } + unit!(@supported $($conversion),+); impl super::Conversion for super::$unit {})+ } @@ -218,7 +228,8 @@ macro_rules! unit { T::new(c.numer().to_biguint().unwrap(), c.denom().to_biguint().unwrap()) } - $(impl $crate::Conversion for super::$unit { + $(unit!(@supported $($conversion),+); + impl $crate::Conversion for super::$unit { type T = T; #[inline(always)] @@ -233,6 +244,7 @@ macro_rules! unit { } } + unit!(@supported $($conversion),+); impl super::Conversion for super::$unit {})+ } @@ -244,7 +256,8 @@ macro_rules! unit { ::from_f64(value).unwrap() } - $(impl $crate::Conversion for super::$unit { + $(unit!(@supported $($conversion),+); + impl $crate::Conversion for super::$unit { type T = V; #[inline(always)] @@ -259,13 +272,15 @@ macro_rules! unit { } } + unit!(@supported $($conversion),+); impl super::Conversion for super::$unit {})+ } storage_types! { types: Complex; - $(impl $crate::Conversion for super::$unit { + $(unit!(@supported $($conversion),+); + impl $crate::Conversion for super::$unit { type T = VV; #[inline(always)] @@ -282,6 +297,7 @@ macro_rules! unit { } } + unit!(@supported $($conversion),+); impl super::Conversion for super::$unit {})+ } }; @@ -297,51 +313,36 @@ macro_rules! unit { #[derive(Clone, Copy, Debug, Hash)] pub struct $unit; }; + (@supported $factor:expr, $base:expr, $scale:expr; $($tt:tt)+) => { + std! { + $($tt)+ + } + }; + (@supported $($conversion:expr),+; $($tt:tt)+) => { + $($tt)+ + }; + (@supported_attr $factor:expr, $base:expr, $scale:expr; $($tt:tt)+) => { + #[cfg(any(feature = "std", feature = "libm"))] + $($tt)+ + }; + (@supported_attr $($conversion:expr),+; $($tt:tt)+) => { + $($tt)+ + }; (@coefficient $factor:expr, $const:expr) => { $factor }; (@coefficient $factor:expr, $base:expr, $scale:expr) => { $factor }; (@coefficient $factor:expr) => { $factor }; (@constant $op:ident $factor:expr, $const:expr) => { $const }; - (@constant $op:ident $factor:expr, $base:expr, $scale:expr) => { - match $op { - $crate::ConstantOp::Add => -0.0, - $crate::ConstantOp::Sub => 0.0, - } - }; + (@constant $op:ident $factor:expr, $base:expr, $scale:expr) => { unit!(@constant $op $factor) }; (@constant $op:ident $factor:expr) => { match $op { $crate::ConstantOp::Add => -0.0, $crate::ConstantOp::Sub => 0.0, } }; - (@base $factor:expr, $base:expr, $scale:expr) => { $base }; (@base $factor:expr, $const:expr) => { 1.0 }; + (@base $factor:expr, $base:expr, $scale:expr) => { $base }; (@base $factor:expr) => { 1.0 }; + (@scale $factor:expr, $const:expr) => { 0.0 }; (@scale $factor:expr, $base:expr, $scale:expr) => { $scale }; - (@scale $factor:expr, $const:expr) => { 1.0 }; - (@scale $factor:expr) => { 1.0 }; - (@logarithmic $factor:expr, $base:expr, $scale:expr) => { - #[inline(always)] - fn into_linear(x: V) -> V { - use $crate::ConversionFactor; - - >::base().pow( - ((<>::T as $crate::num::One>::one() - / >::scale()) - * x.into_conversion()) - .value()) - } - - #[inline(always)] - fn from_linear(x: V) -> V { - use $crate::ConversionFactor; - - (>::scale() - * x.into_conversion() - .log(>::base().value()) - .into_conversion()) - .value() - } - }; - (@logarithmic $factor:expr, $const:expr) => { }; - (@logarithmic $factor:expr) => { }; + (@scale $factor:expr) => { 0.0 }; }