Skip to content

Arithmetic rules for Difference versus Absolute quantities

Werner Keil edited this page Oct 2, 2024 · 4 revisions

This page is adapted from Andi's page on Indriya. Differences are documented on issue 140. In this page, arithmetic rules and unit conversions are exemplified with Temperature but applies more generally.

Quantity and Unit properties

Assuming MeasurementType enumeration with two values (ABSOLUTE and INCREMENTAL/RELATIVE) being a property of Quantity:

/**
 * Hint about whether a measurement is absolute or incremental.
 * Different conversion rules apply to quantities depending
 * on whether they are absolute or incremental measurement.
 */
public enum MeasurementType {
  /**
   * Quantity measured on an absolute scale, in which a true minimum is known to exist.
   * Absolute scale begins at its minimum (typically, but not necessarily, zero)
   * and progresses in only one direction.
   * 
   * <p>Example: an absolute quantity of unit CELSIUS, where 0°C is equivalent 273.15 K.</p>
   *
   * @see <a href="https://en.wikipedia.org/wiki/Absolute_scale">Absolute scale on Wikipedia</a>
   */
  ABSOLUTE,
  
  /**
   * Quantity measured as the difference between two other quantities.
   * The other quantities may be absolute or incremental.
   *
   * <p>Example: incremental quantity of unit CELSIUS, where 0°C is equivalent 0 K.</p>
   */
  INCREMENTAL
}

Assuming LevelOfMeasurement enumeration with four values (NOMINAL, ORDINAL, INTERVAL, RATIO) being a property of Unit. For the purpose of this discussion, only the INTERVAL and RATIO matter; the two other levels of measurement are ignored. The LevelOfMeasurement defines which operations are allowed on ABSOLUTE quantities, as listed on the Wikipedia page. The level of measurement does not directly said which operations are allowed on INCREMENTAL quantities; it is not its purpose.

Each unit is associated to one and only one level of measurement. The level of measurement of °C is INTERVAL; there is no RATIO °C. The level of measurement of K is RATIO; there is no INTERVAL K. We do not duplicate Unit instances for different level of measurements.

Two Quantity instances can have the same Unit but different MeasurementType. We can have an absolute quantity of 5°C and an incremental quantity of 5°C. Those two quantities share the same °C units, but have different conversion rules to K.

For all Unit instances, the following relationship shall hold:

  • unit.getConverterTo(unit.getSystemUnit()).isLinear() == true implies unit.getLevelOfMeasurement() == RATIO. Note that the isLinear() method is misnamed; it behavior is more like a isScale() method.

Quantity equivalence

Two quantities are considered equivalent if, after conversion to system units, their value are equal. For example 1 inch is equivalent to 2.54 cm. Whether 1°C is equivalent to 1 K depends if 1°C is an ABSOLUTE or INCREMENTAL quantity.

Arithmetic rules

The arithmetic rules between Quantity instances SHALL obey to arithmetic laws, in particular commutativity and associativity. For example A + B must produce a result equivalent to B + A. The reason for this requirement is that Quantity should be usable as drop-in replacements for the double primitive type in a program. If a program uses equations like E₁=½kT₁ and E₂=½kT₂ then wants to compute E₁+E₂, it may want to rearrange the equation as E₁+E₂=½k(T₁+T₂). But this is possible only if the numbers obey the arithmetic laws of associativity, otherwise the result is unpredictable. It may even be difficult to said which equation is correct.

Arithmetic laws (commutativity, associativity…) must apply on two Quantity components:

  1. The numerical value.
  2. The MeasurementType of the result (absolute or incremental).

Arithmetic laws does not need to apply to the unit of measurement as long as the results are equivalent. For example when computing 2 m + 3 cm, it is correct to have one implementation giving 2.03 m and another implementation giving 203 cm since both results are equivalent. Having different units of measurements, even reused for more computation, will not cause the implementations to produce non-equivalent results.

Arithmetic rules on the numerical value

The golden rule is:

The numerical values of all arithmetic operations must be calculated as is all values were converted to system unit before calculation. The conversion (not the arithmetic) depends on the MeasurementType. For example the conversion of 0°C can be 273.15 K or 0 K depending if the measurement type is ABSOLUTE or INCREMENTAL respectively.

Implementations are free to avoid conversions as long as the result is numerically equivalent. Implementations are also free to express the result in the unit of their choice, since this freedom does not break arithmetic laws.

This rule assumes that the LevelOfMeasurement of all system units is RATIO. For example it is illegal to define a UnitSystem with °C as a system unit.

Arithmetic rules on the Quantity type

Let:

  • scalar … a number or dimensionless quantity
  • A … absolute Quantity type, exemplified with CELSIUS, unit °C.
  • D … incremental Quantity type, exemplified with CELSIUS, unit °C. The Δ is put in front of the number to emphasize that the incremental characteristic is put on the quantity, not on the unit. So Δ2°C can be read as "increment of 2°C".

Assuming commutativity of addition and multiplication, and let X - Y === X + (-1 * Y)

Additions and subtractions

  • A ± D -> A … 2°C + Δ3°C = 5°C
  • D ± A -> A … Δ2°C + 3°C = 5°C
  • D ± D -> D … Δ2°C + Δ3°C = Δ5°C
  • A ± A -> A … 0°C + 0°C = 273.15°C (discussed later)

Arithmetic law checks:

  • A ± D = D ± A
  • A + -1*D = A - D

Scalars

  • A * scalar -> A … 0°C * 2 = 273.15°C
  • D * scalar -> D … Δ5°C * 2 = Δ10°C
  • D / D -> scalar … Δ10°C / Δ2°C = 5
  • A / A -> scalar … by converting operands to system unit = Kelvin
  • A / D -> scalar … by converting operands to system unit = Kelvin
  • D / A -> scalar … by converting operands to system unit = Kelvin

Multiplications and divisions

Reminder: all operations shall convert operands to system unit = Kelvin. The conversion however differs depending if the operand is A or D.

  • A * A -> A … yields unit K²
  • D * D -> D … yields unit K²
  • A * D -> D … yields unit K²
  • D * A -> D … yields unit K²
  • 1 / A -> A … yields unit 1/K
  • 1 / D -> D … yields unit 1/K

Arithmetic law checks:

  • A * D = D * A
  • 1/(1/A) = A
  • 1/(1/D) = D
  • A/(1/D) = A * D

Addition and subtraction of absolute quantities

This is a non-intuitive case. Intuitively we would expect A - A -> D because subtractions between two quantities are usually for computing differences (or increments). But defining A - A -> D while keeping A + A -> A causes an arithmetic contradiction, since we would get A - AA + -1*A. To avoid this contradiction we need to define:

  • A ± A -> A … 0°C - 0°C = -273.15°C

This require a new difference operator (other than Quantity.subtract to allow for the intended use-case of generating a INCREMENTAL quantity from 2 ABSOLUTE quantities:

  • Q1.subtract(Q2) … 5°C - 4°C = -272.15°C
  • Q1.difference(Q2) … 5°C - 4°C = Δ1°C

API extension

The current QuantityFactory.create(Number, Unit) method stay basically unchanged. It would create a quantity of type ABSOLUTE. The following new methods would be added:

  • QuantityFactory.createIncrement(Number, Unit) creates an INCREMENTAL quantity.
  • Quantity.difference(Quantity) as discussed above.
  • UnitConverter.deltaConvert(double) required for implementations of arithmetic operations involving INCREMENTAL quantities.