iliekturtles/uom

Conversion factor doc inconsistency

NiklasVousten opened this issue · 2 comments

The documentation stated that Conversion is handled like this: (value * coefficient()) + constant()

But using this code snipped for a manual conversion like Fahrenheit to Kelvin this breaks:

fn test() {
    let f: f32 = 2.0;
    let k = convert::<uom::si::thermodynamic_temperature::degree_fahrenheit>(f);
    println!("{k:?}");
}

fn convert<N>(value: f32) -> f32
where
    N: uom::Conversion<f32, T = f32>,
{
    (value * N::coefficient()) + N::constant(uom::ConstantOp::Add)
}

The result is 460.78113, but 2 degrees Fahrenheit is 256.483

Rewriting the equation to (value + constant()) * coefficient() results in 256.48337

So there is an inconsistency between code and documentation.

This was first tested on 0.34.0, but inconsistency is also present in 0.35.0

Good catch. The actual code to convert to from a given unit to the base unit is in the to_base function linked below and essentially does (value + constant()) * coefficient() as you noted. The more complex formula in the code is to handle floating point precision issues and non-standard base units.

A PR to update the documentation for the Conversion trait is welcome.

uom/src/system.rs

Lines 328 to 357 in 6d34c3e

/// Convert a value from the given unit to base units.
///
/// ## Generic Parameters
/// * `D`: Dimension.
/// * `U`: Base units.
/// * `V`: Value underlying storage type.
/// * `N`: Unit.
#[inline(always)]
fn to_base<D, U, V, N>(v: &V) -> V
where
D: Dimension + ?Sized,
U: Units<V> + ?Sized,
V: $crate::Conversion<V>,
N: $crate::Conversion<V, T = V::T>,
{
use $crate::typenum::Integer;
use $crate::{Conversion, ConversionFactor};
let v = v.conversion();
let n_coef = N::coefficient();
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()
}
}

Converting from a base unit to a given unit essentially does (value * coefficient()) - constant.

uom/src/system.rs

Lines 297 to 326 in 6d34c3e

/// Convert a value from base units to the given unit.
///
/// ## Generic Parameters
/// * `D`: Dimension.
/// * `U`: Base units.
/// * `V`: Value underlying storage type.
/// * `N`: Unit.
#[inline(always)]
fn from_base<D, U, V, N>(v: &V) -> V
where
D: Dimension + ?Sized,
U: Units<V> + ?Sized,
V: $crate::Conversion<V>,
N: $crate::Conversion<V, T = V::T>,
{
use $crate::typenum::Integer;
use $crate::{Conversion, ConversionFactor};
let v = v.conversion();
let n_coef = N::coefficient();
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()
}
}