-
-
Notifications
You must be signed in to change notification settings - Fork 41
Arithmetic operations on Quantity
This is a request to verify that the group agrees on the following points. I suggest to verify if we have agreement in the order below (e.g. it is useless to move to item 5 if we don't agree on item 4):
Two quantities A and B are said equal if the have equal value as defined by <the number type>.equals(Object)
and equal unit of measurement as defined by Unit.equals(Object)
.
In pseudo-code: A.getValue().equals(B.getValue())
and A.getUnit().equals(B.getUnit())
Proposal: Two quantities A and B are said equivalent if, after conversion to the same unit of measurement by a call to Quantity.to(Unit)
, their numerical values are the same (ignoring rounding errors, overflow and underflow).
In pseudo-code: A.to(commonUnit).getValue()
≈ B.to(commonUnit).getValue()
Note: A is equal to B implies that A is equivalent to B. But the converse is not necessarily true: A is equivalent to B does not imply that A is equal to B.
Example: 2 km is equivalent to 2000 m. But 2 km is not equal to 2000 m in the usual sense of Java Object.equals(Object)
. For the remaining of this page, the Java sense of equals
will not be used.
The Units of Measurement API 2.0 (JSR 385) SHALL describe all Quantity
arithmetic operations with enough details for ensuring that different implementations produce at least equivalent results (issue #98).
Note: whether different implementations should produce equal results is not the purpose of this page. If desired, it can be a separated discussion. For the purpose of mathematical discussion in this page, only equivalence is needed.
The specification of Quantity
arithmetic operations SHALL be consistent with arithmetic laws. For example Quantity.add(Quantity)
shall be defined in such a way that A + B is equivalent to B + A (addition commutativity) for any quantities, even if their units of measurement are not the same (issue #99).
Example: mean kinetic energy in joules (J) is related to kinetic temperature in kelvins (K) by E = 1.5*k*T where k ≈ 1.38E−23 J/K. One may want to compute the total kinetic energy of two systems (the nature of N below is not the purpose of this discussion):
E₁ + E₂ = (N*k*T₁) + (N*k*T₂)
If we can rely on arithmetic laws, it is safe to reorganize the equation as below in order to save two multiplications:
E₁ + E₂ = (N*k) * (T₁ + T₂)
But if we can not rely on distributivity or other arithmetic laws, then re-arranging the equation as above have unpredictable consequences. Those two equations may produce different results, and it may even be difficult for the user to determine which one is correct.
A.add(B)
operations defined by the following rules can be shown to be in violation with arithmetic laws when A and B use different units of measurement and at least one unit is a "shifted units". The most emblematic examples are arithmetic operations on temperature measurements in Kelvin et Celsius degrees, but they are not the only examples.
- "Convert the unit of second operand (B) to the units of the first operand (A)" — this rule breaks addition commutativity and associativity.
- "Interpret the first operand (A) as a measurement and the second operand (B) as an increment" — this rule breaks addition commutativity and associativity.
- Other rules have been explored in issue #95.
I believe that the only rule compliant with arithmetic laws in current API (possible API changes would be a separated discussion) is:
-
"Perform calculations AS IF all values were converted to system unit before the arithmetic operation."
- Note 1: it does not mean that the result must be in system unit; implementations are free to return whatever equivalent quantity they wish.
- Note 2: it does not mean that implementations must convert all values to system unit. Implementations are free to use whatever strategy produce equivalent results.
Alternatives rules can be proposed. If we can not find arithmetic laws violated by the alternative proposal, the alternative will be accepted as a valid choice for consideration. But until now I'm not aware of any alternative that do not involve API change.
Acknowledge that the consequence of applying arithmetically consistent operations without API change is that 1°C + 2°C = 276.15°C and 2×3°C = 279.15°C. Acknowledge that there is no arithmetically consistent alternative found so far. Acknowledge that even if counter-intuitive, this is an unavoidable mathematical consequence unless we change the API.
Note: while counter-intuitive, above-cited consequences on temperature measurements are actually consistent with thermodynamic laws. For example thermodynamic equations work with temperatures in Kelvin, not in Celsius. So the temperature have to be converted in Kelvin before to be used in an equation like E = 1.5*k*T (section 4 above) in order to be physically valid.
If the group does not want to expose users to results like "1°C + 2°C = 276.15°C", we have a limited number of options. The remaining of this page list all options proposed so far (this section will be edited if new options are proposed), excluding arithmetically inconsistent options:
- Declare 1°C + 2°C as an invalid operation.
- Problem 1: adding temperature measurements is a valid operation when computing an average temperature, computing interpolations or applying thermodynamic formulas.
- Problem 2: declaring 1°C + 2°C as an invalid operation would introduce an asymmetry with subtraction, where 2°C - 1°C is a valid operation.
- Encourage implementations to return the result of 1°C + 2°C in Kelvin instead of Celsius degrees. Users may guess more easily what is happening if they see 549.3 K instead of 276.15°C.
- Introduce API change for allowing implementations to differentiate measurements from differences (or increments). The API change may be a new type or an enumeration value; any solution that allow
Quantity.add(Quantity)
to know for example that the 2°C value in "1°C + 2°C" is an increment rather than a measurement may fit. Details of such API change would be a separate discussion.