Introduction to JSR-275: Measures and Units

New API specification removes uncertainty from programming with physical measurements

Page 3 of 4

For convenience, the SI class provides static multiplier methods, such as KILO(Unit) or CENTI(Unit), equivalent to the multiplication or division of the unit by a power of 10. For example, KILO(METER) is equivalent to METER.times(1000), whereas CENTI(METER) is equivalent to METER.divide(100).

As Listing 7 shows, you can also create new units with a new symbol to distinguish between quantities of a different nature but of the same dimension (AlternateUnit) or by combining several units together (CompoundUnit). CompoundUnit is used exclusively for formatting purposes (see Parsing and formatting).

Listing 7. Creating new units and symbols

 Unit<Angle> RADIAN = ONE.alternate("rad");
Unit<Pressure> PASCAL = NEWTON.divide(METER.pow(2)).alternate("Pa");
Unit<Duration> HOUR_MINUTE_SECOND = HOUR.compound(MINUTE).compound(SECOND);
Unit<Angle> DEGREE_MINUTE_ANGLE = DEGREE_ANGLE.compound(MINUTE_ANGLE);

Figure 1 shows a UML diagram of the unit package.

A UML diagram of the unit package
Figure 1. Unit package (click for a larger image)

Given two units of same dimensions, you can retrieve the UnitConverter between the two units. Then you can use the converter to perform conversions of double values, as shown in Listing 8.

Listing 8. Converting double values with UnitConverter

 double distanceInMiles = 23.0;
UnitConverter mileToKilometer = MILE.getConverterTo(KILO(METER));
double distanceInKilometers = mileToKilometer.convert(distanceInMiles);

For more complex numeric types such as BigDecimal, conversions are more elaborate and are performed by the Measure implementation directly. For example, the Measure implementation for BigDecimal takes into account the MathContext holding the precision and the rounding mode. It should be noted that most UnitConverters between predefined units are instances of RationalConverter (the ratio of two integer numbers) and as such do not introduce conversion errors of their own. When you create new derived units, it's recommended that you use integer multipliers or divisors whenever possible instead of double-value multipliers. Often decimal numbers don't have an exact binary representation (IEE754 format for double), and units derived from such inexact numbers could introduce numeric error artifacts during conversions. Listing 9 shows a 128-bit conversion from meters to feet using BigDecimal.

Listing 9. BigDecimal conversion

 Unit<Length> FOOT = METER.times(3048).divide(10000); // Exact.
BigDecimal meters = new BigDecimal("9192631770.03345677");
MathContext ctx = MathContext.DECIMAL128;
Measure<BigDecimal, Length> m = Measure.valueOf(meters, METER, ctx);
BigDecimal feet = m.to(FOOT).getValue();

The numeric value for Measure does not need to be a scalar. It can be an array, for example, in which case the conversion of the whole array can be performed with a single operation (which is also efficient, because the converter is calculated only once). Listing 10 shows the conversion of a velocity vector.

Listing 10. Vector conversion

 Measure<double[], Velocity> velocity3D 
     = Measure.valueOf(new double[] { 0.2, -0.3, 0.5 }, METER_PER_SECOND);
double[] milesPerHour = velocity3D.to(MILE.divide(HOUR)).getValue();

Unit converters

Unit converters have two purposes: converting between units and creating new derived units (TransformedUnit).

Most users don't need to instantiate UnitConverter, because convenience methods such as Unit.times(double) or Unit.divide(long) do this for you. The predefined converters in the converter package (shown in Figure 2) should be sufficient for most applications. However, you can create custom converters. For example, embedded systems might use a device-specific converter whose value changes with temperature. It is safe for an application to work with the device units (not corrected) and/or the standard units (corrected). Measures stated in both units cannot be confused or mixed without explicit conversions.

Predefined converters in the converter package
Figure 2. Converter package (click for a larger image)

Parsing and formatting

Unit and Measure parsing and formatting follow the standard Java paradigm for formatting locale-sensitive information such as dates, messages and numbers. (A Locale object represents a specific geographical, political or cultural region. An operation that requires a Locale to perform its task is called locale-sensitive and uses the Locale to tailor information for the user.) Two abstract classes derived from java.util.Format -- UnitFormat and MeasureFormat -- provide the locale-sensitive instances as well as locale-insensitive instances to facilitate unambiguous electronic communication of quantities together with their units (UCUM format). The default format used by the Unit.toString() and Unit.valueOf(String) methods is locale-sensitive and should not be used to send information over the Atlantic; otherwise a measure such as 1.0 Gal (about 3.78 liters in the United States) would be interpreted as about 4.54 liters in the United Kingdom (1 UK gallon)!

| 1 2 3 4 Page 3