unitsofmeasurement / indriya

JSR 385 - Reference Implementation
Other
119 stars 40 forks source link

Scaling / multiplication is unable to apply factors to temperatures #424

Open milgner opened 1 month ago

milgner commented 1 month ago

Scenario: source data is in tenths of degree celsius. Trying to convert it to degrees celsius results in

java.lang.UnsupportedOperationException: ℃ is non-linear, cannot convert

Example Test (Kotlin):

ReBasedUnitFormat is just a utility class around EBNFUnitFormat which uses a custom SymbolMap

class UnitsSpec : DescribeSpec({
    it("can deal with centidegrees Celsius") {
        val source: MeasureUnit<*> = ReBasedUnitFormat.parse("°C/10") 
        val dest: MeasureUnit<*> = ReBasedUnitFormat.parse("°C")

        source.getConverterToAny(dest).convert(10).shouldBeEqual(1.0)
    }
})

It seems like it should be possible to do this? Is there a way to achieve this with units of measurement without resorting to external scale factors?

keilw commented 1 month ago

Which Kotlin framework are you using here, Physikal?

I have not looked into that for a while, but MeasureUnit does not feel familiar.

milgner commented 1 month ago

Which Kotlin framework are you using here, Physikal?

I have not looked into that for a while, but MeasureUnit does not feel familiar.

Sorry, I should have mentioned: MeasureUnit is just a type alias defined as

typealias MeasureUnit<T> = javax.measure.Unit<T>

in order to avoid confusion with the Kotlin Unit type.

keilw commented 1 month ago

So you don't use an extra layer or framework?

Since CELSIUS uses an AddConverter because 0 Degree Celsius are 273.15 Kelvin, that converter is not linear, that seems the problem here. @andi-huber, @desruisseaux Any thoughts, is everything correct in ProductUnit.getSystemConverter() ?

thedanielhanke commented 1 month ago

out of interest, why is this not linear?

A function is linear if it can be written in the form f(x) = mx + b, where:

adding 273.15 to a value x can be written as

f(x) = 1x + 273.15

what am i missing?

keilw commented 1 month ago

See the answer here: https://www.geeksforgeeks.org/what-is-the-relationship-between-celsius-and-kelvin-scale-of-temperature/

the Celsius scale is not linearly dependent with absolute zero and may give false results.

thedanielhanke commented 1 month ago

i see this for celsius and kelvin, thank you. <3

however, celsius to fractions of celsius should not apply to this, right?

desruisseaux commented 1 month ago

I'm not familiar with Indriya implementation, but I think that when deriving units, it is also okay to apply a multiplication factor for units with offset. Even by application of the rule that calculation must be equivalent to a calculation in the system unit, we have:

The offsets before and after the multiplication cancel.

Note: The definition of "is linear" in the Unit API is at odd with mathematics. An equation of the form "T × scale + offset" is still linear. I do not remember why the group decided to call a unit as "non linear" as soon as its conversion to system unit has a non-zero offset, but it still disturb me.

keilw commented 1 month ago

@desruisseaux Thanks for the input. As this is an implementation detail and not in the spec, we could change that.

I guess it's mostly because of the 0 °C + 0 °C = 273.15 °C (546.30 K) 0 °C + 0 °C = 0 °C (273.15 K) 0 °C + 0 °C = 0 °C (0 K) paradoxon.

desruisseaux commented 1 month ago

Yes, when applying additions/subtractions, the result depends on whether the unit is an absolute measurement or an interval. But multiplications are sometime simpler because a cancellation of the offset happens more easily (not always, but in the case of scaling to deci-, milli-, etc., I think it is the case).

thedanielhanke commented 1 month ago

So is this a bug which should be fixed?