zjuhasz
zjuhasz

Reputation: 1549

Java units of measurement library addition and subtraction returning incorrect values

I'm using the reference implementation of JSR363. I've tried many variations of this but I'll give this code as an example.

ServiceProvider provider = ServiceProvider.current();
QuantityFactory<Length> lengthFactory = provider.getQuantityFactory(Length.class);
Quantity<Length> first = lengthFactory.create(5, Units.METRE.divide(100.0));
Quantity<Length> second = lengthFactory.create(3, Units.METRE);
System.out.println(second.add(first));

This prints 503.0 m. Clearly something is very wrong and this should be 3.05 m. I find it very hard to believe that this is actually a bug with the library and I'm hoping someone can tell me what I'm missing.

Upvotes: 5

Views: 1319

Answers (4)

Werner Keil
Werner Keil

Reputation: 117

Fixed with release 1.0.1 of the JSR 363 RI.

Upvotes: 1

Werner Keil
Werner Keil

Reputation: 642

Thanks for the detailed description on how to reproduce the issue.

The NumberQuantity class was reported to cause a similar issue by another developer (who explained his problem by email) So we're aware of things to improve and when the fix is complete, we'll issue a service release for the RI.

Regards, Werner

Upvotes: 1

omahdi
omahdi

Reputation: 630

Adding to the insight provided by Beryllium, I can confirm the reported behavior and have tracked down the problem to a bug in the reference implementation. I took the liberty of reporting the issue and my findings along with a proposed fix.

Indeed, the method doubleValue(Unit<Q>) of NumberQuantity, which is used under the hood in conversion steps, erroneously computes the inverse transformation between units. This explains OP's observation that multiplication and division by constant factors appear to be reversed when applied as unit transformations.

Since the default quantity factory exclusively returns objects of type NumberQuantity, all quantities created this way are affected. However, other types like DoubleQuantity seem to work correctly. As suggested in the answer by Beryllium, they can be created by means of Quantities.getQuantities(<value>, <unit>). The following code snippet demonstrates that conversion breaks for a NumberQuantity obtained from the default factory, but works correctly for a DoubleQuantity:

package test.jsciunits;

import javax.measure.spi.ServiceProvider;
import javax.measure.Quantity;
import javax.measure.quantity.Length;
import javax.measure.spi.QuantityFactory;
import tec.units.ri.quantity.Quantities;
import static tec.units.ri.unit.Units.*;
import static tec.units.ri.unit.MetricPrefix.*;

public class JScienceUnits {
    public static void main(String[] args) {
        ServiceProvider provider = ServiceProvider.current();
        QuantityFactory<Length> lengthFactory = provider.getQuantityFactory(Length.class);

        Quantity<Length> q = lengthFactory.create(5.0, METRE);
        Quantity<Length> r = Quantities.getQuantity(5.0,  METRE);

        System.out.println("q = " + q + ", q.to(CENTI(METRE)) = " + q.to(CENTI(METRE)));
        System.out.println("r = " + r + ", r.to(CENTI(METRE)) = " + r.to(CENTI(METRE)));
    }
}

This produces

q = 5.0 m, q.to(CENTI(METRE)) = 0.05 cm
r = 5.0 m, r.to(CENTI(METRE)) = 500.0 cm

where the second line shows a correct conversion result.

Upvotes: 2

Beryllium
Beryllium

Reputation: 566

After looking into this a bit I've been able to reproduce the oddities in question. It appears that using the multiply() or the divide() methods when passing a Unit into a QuantityFactory has strange effects. In example:

Quantity firstQuant = quantFactory.create(10.0,Units.METRE)
Quantity secondQuant = quantFactory.create(20.0,Units.METRE.divide(10.0))
System.out.println(secondQuant.add(firstQuant))

outputs the following: 20.5 dm. Even when using MetricPrefix, which seems to be the default method of setting non base SI units, it seems to generate extremely inaccurate Units. Using the following:

Quantity secondQuant = quantFactory.create(20.0,MetricPrefix.KILO(Units.METRE))

outputs 10020.0 km which is nowhere near accurate. However, the following:

Quantity firstQuant = quantFactory.create(10.0,Units.METRE)
Quantity secondQuant = quantFactory.create(20.0,Units.METRE)
System.out.println(secondQuant.divide(10.0).add(firstQuant))

outputs 12.0 m, which is obviously the correct answer.

In honesty the best solution is to simply not use those operations in the creation of the Quantity and to convert to other units of measurement using the built in getConverter() of MetricPrefix enums.

The better way to create Quantities is to use Quantities.getQuantities()

Upvotes: 4

Related Questions