-
-
Notifications
You must be signed in to change notification settings - Fork 41
Arithmetic rules for Difference versus Absolute quantities
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.
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
impliesunit.getLevelOfMeasurement()
==RATIO
. Note that theisLinear()
method is misnamed; it behavior is more like aisScale()
method.
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.
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:
- The numerical value.
- 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.
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 isABSOLUTE
orINCREMENTAL
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.
Let:
- scalar … a number or dimensionless quantity
-
A … absolute
Quantity
type, exemplified withCELSIUS
, unit °C. -
D … incremental
Quantity
type, exemplified withCELSIUS
, 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)
-
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
-
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
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
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 - A
≠ A + -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
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 anINCREMENTAL
quantity. -
Quantity.difference(Quantity)
as discussed above. -
UnitConverter.deltaConvert(double)
required for implementations of arithmetic operations involvingINCREMENTAL
quantities.