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 logarithmic units #310

Closed
wants to merge 2 commits into from
Closed
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
108 changes: 106 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,28 @@ pub trait Conversion<V> {
<Self::T as crate::num::Zero>::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)]
fn base() -> Self::T {
<Self::T as crate::num::Zero>::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)]
fn scale() -> Self::T {
<Self::T as crate::num::Zero>::zero()
}

/// Instance [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html).
///
/// Default implementation returns the coefficient: `Self::coefficient()`.
Expand Down Expand Up @@ -480,6 +502,16 @@ pub trait ConversionFactor<V>:
#[must_use = "method returns a new number and does not mutate the original value"]
fn powi(self, e: i32) -> Self;

/// Raises a `ConversionFactor<V>` to a power.
#[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<V>` of a value.
#[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<V>` into its underlying storage type.
#[must_use = "method returns a new number and does not mutate the original value"]
fn value(self) -> V;
Expand Down Expand Up @@ -545,6 +577,18 @@ storage_types! {
<V as crate::num::Float>::powi(self, e)
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn pow(self, e: Self) -> Self {
<V as crate::num::Float>::powf(self, e)
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn log(self, base: Self) -> Self {
<V as crate::num::Float>::log(self, base)
}

#[inline(always)]
fn value(self) -> V {
self
Expand All @@ -571,7 +615,19 @@ storage_types! {
impl crate::ConversionFactor<V> for crate::num::rational::Ratio<V> {
#[inline(always)]
fn powi(self, e: i32) -> Self {
self.pow(e)
crate::num::rational::Ratio::<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)]
Expand Down Expand Up @@ -607,6 +663,18 @@ storage_types! {
}
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn pow(self, e: Self) -> Self {
crate::num::rational::Ratio::<V>::pow(&self, e)
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn log(self, base: Self) -> Self {
crate::num::rational::Ratio::<V>::log(&self, base)
}

#[inline(always)]
fn value(self) -> V {
self.to_integer()
Expand All @@ -629,7 +697,19 @@ storage_types! {
impl crate::ConversionFactor<V> 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)]
Expand Down Expand Up @@ -661,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
Expand Down Expand Up @@ -695,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
Expand Down
16 changes: 11 additions & 5 deletions src/quantity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"`).
Expand Down Expand Up @@ -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,
Expand All @@ -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",
}
Expand Down
7 changes: 7 additions & 0 deletions src/si/electric_potential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ 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: 7.746_E-1, 10.0, 20.0; "dBu", "decibel-unit", "decibel-units";
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/si/power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ 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; "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",
"foot pounds-force per hour";
Expand Down
8 changes: 7 additions & 1 deletion src/si/ratio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, 10.0; "dB", "decibel", "decibels";
}
}

Expand Down Expand Up @@ -180,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;
Expand Down Expand Up @@ -217,6 +219,10 @@ mod tests {
Test::assert_eq(&Ratio::new::<r::ratio>(V::one()
/ V::from_f64(1.0_E15).unwrap()),
&Ratio::new::<r::part_per_quadrillion>(V::one()));
Test::assert_eq(&Ratio::new::<r::ratio>(V::one()),
&Ratio::new::<r::decibel>(V::zero()));
Test::assert_eq(&Ratio::new::<r::ratio>(V::from_u8(10).unwrap()),
&Ratio::new::<r::decibel>(V::from_u8(10).unwrap()));
}
}

Expand Down
61 changes: 47 additions & 14 deletions src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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! {
Expand Down Expand Up @@ -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
Expand Down
Loading