Reputation: 1549
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
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
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
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