ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Apples and Oranges (and the Java Units Specification)

by Ori Kushnir
01/07/2004

This article discusses JSR-108, the Java Units Specification, which allows developers to create systems of units and to define conversion and representation rules in Java. Using an implementation of the Java Units spec, you would be able to attach a unit to a number so that when defining a rectangle in your program, it is clear that its length equals six feet, six meters, six miles, six light years, or some other standard unit of length, rather than just six. Following the definition of fundamental concepts, we will present this JSR, discuss possible uses and limitations, and provide ideas for future extensions. The article should be of interest to developers involved with scientific, financial, industrial, or other applications where units are of importance.

Units and Dimensions

Your second-grade teacher must have told you that you cannot compare a pound of apples with a pound of oranges. This may have been your first encounter with the concept of dimensions. Some years later, your Physics 101 professor probably mentioned that you cannot directly compare speed measured in furlongs per fortnight to speed measured in miles per hour. She was talking about units.

The concepts of dimensions, things that can be measured and units, and ways to express these measurements play a central role in science, engineering, and other areas of life. Indeed, it is a well-publicized fact that incompatible units can lead to disaster, such as the loss of the Mars orbiter in 1999 due to confusion at NASA between the metric system and the English system.

Many modern programming languages, including Java, ignore units and dimensions, treating numbers as being dimensionless. This gives developers the freedom to mistakenly turn feet into pounds, just as years ago, in the days of pre-ANSI C, a developer could turn a character string into meaningless floating point numbers with no warning. JSR-108, the main subject of this article, is the Java community's attempt to tackle this issue, but before we discuss it, some definitions are in order.

A dimension is a thing that can be measured. Length, time, mass, temperature, air pressure, and velocity are all dimensions. Dimensions can be related to other dimensions using multiplication and division. For example, length equals velocity multiplied by time, regardless of how we measure them.

A unit, or unit of measurement, is a constant quantity that allows us to express measurements of a dimension -- for example, the current definition of the meter, a standard unit of length, is:

A meter is the length of the path traveled by light in vacuum during a time interval of 1/299,792,458 of a second.

Units can be converted into other units that measure the same dimension. For example, we can convert IPS (Instructions Per Second) into MIPS by dividing the number of IPS by 1 million. So: 7,000,000 IPS = 7 MIPS.

Units can be derived from other units, mostly by using multiplication and division. For example, the standard unit of Force can be defined as:

1 Newton = 1 Kilogram * Meter / (Second * Second)

Units are compatible or convertible if their dimensions are equal. A Newton that has dimension mass*length/time2 is therefore compatible with a Dyne (that's another unit of force), but not with a Didot point, which has a dimension of length.

Units can be aggregated to form systems of units, which allow us to measure all of the dimensions in which we are interested. The standard system of physical units is known as SI and deals with mass, time, length, electric current, amount of material, and a couple of other physical dimensions. Newtons and meters are part of SI, while degrees Fahrenheit and inches (and, for that matter, Furlongs and Didot points) are not.

Finally, there exist dimensionless quantities, which have no dimension (or have the identity dimension). Seven, as such, is a dimensionless quantity.

JSR-108

The Java Units Specification, initiated in early 2001, is an attempt at introducing physical units and dimensions to the language. At its base were two reference-unit frameworks (see References), both developed by Steven Emmerson, who is also the JSR-108 specification lead. A concrete implementation for physical units was later developed by Jean-Marie Dautelle as part of the noteworthy J.A.D.E. package. JSR-108 introduces classes that support:

  • Checking unit compatibility.
  • Numeric value conversion.
  • Scaling and other arithmetic operations on units.
  • Implementation of standard units (e.g., units that are part of SI).
  • Unit parsers for standard specification formats.

For example, consider the following code snippet (using the J.A.D.E. package):

// Initialize the JADE library
JADE.initialize();

// Declare some units
Unit unit1, unit2, unit3, unit4;

// Make the first unit Joule hour per meter         
unit1 = SI.JOULE.divide(SI.METER);
unit1 = unit1.multiply(NonSI.HOUR);

// Make the second unit Horsepower second 
// squared per Didot
unit2 = NonSI.DIDOT.pow(-1);
unit2 = unit2.multiply(NonSI.HORSEPOWER)
              .multiply(SI.SECOND.pow(2));

// unit3 is a thousand unit1
unit3 = unit1.multiply(1000); 

// unit4 is parsed from a standard
// specification string
unit4 = Unit.valueOf("1 kg/m/s2*rad");

// Check unit compatibility
System.out.println(
    "Are the units compatible? " + 
    unit1.isCompatible(unit2));

// convert 100 unit1 to unit2
Quantity q = Quantity.valueOf(100, unit1);
System.out.println(q.toString(unit1) + 
                   " is equal to " + 
                   q.toString(unit2));
	   
// add 100 J·h/m to 100 hp·s2/Didot
// note that this is possible because the
// units are compatible and that conversion
// is automatic! We express the result in J·h/m.
Quantity q2 = Quantity.valueOf(100, unit2);
q2.add(q);
System.out.println("The sum of 100 " + unit1 + " and 100 " 
                    + unit2 + " is " + q2.toString(unit1));                    

// convert 100 unit1 to unit3
System.out.println(q.toString(unit1) + " is equal to " + q.toString(unit3));

// what's unit4?              
System.out.println(unit4);

The preceding code will result in the following output:

Are the units compatible? true

100.0000000000000 J·h/m is equal to 0.1839991624733684 hp·s2/Didot

The sum of 100 J·h/m and 100 hp·s2/Didot is 54348.0734671680 J·h/m

100.0000000000000 J·h/m is equal to 0.1000000000000000 [J·s/m*3600000.0]

kg·s2·rad/m

Of course, unit conversions, while important and useful, are only part of the picture. JSR-108 also allows us to write much cleaner, safer code. For example, let's look at the parameter list for this calcAcceleration() method:

/**
 * 
 * @param initialVelocity initial velocity in 
 * meters per second
 * @param finalVelocity final velocity in 
 * meters per second
 * @param duration duration in seconds
 * @return acceleration in meters per second^2
 */

public static double calcAcceleration(
double initialVelocity,
double finalVelocity,
double duration) {        
 
   if (duration <= 0) {
         throw new IllegalArgumentException(
            "Duration must be positive"
       );
   } 
   return (finalVelocity - initialVelocity)/(duration);                 
}

To use it correctly, a developer would have to consult the Javadoc, both for figuring out the order of parameters and for checking the units that need to be used. I'm sure that every reader of this article has experienced getting this wrong. In the JSR-108/J.A.D.E. world, the method would become:

/**
 * 
 * @param initialVelocity initial velocity
 * @param finalVelocity final velocity
 * @param duration duration 
 * @return acceleration
 */

public static Acceleration calcAcceleration(Velocity initialVelocity,
Velocity finalVelocity, 
Duration duration) {

    if (duration.doubleValue() <= 0) {
        throw new IllegalArgumentException(
        "Duration must be positive"
        );
    } 

    return Acceleration.accelerationOf(
            finalVelocity.subtract(initialVelocity).
            divide(duration)
            );
}

Note that we no longer need to check our units in the Javadoc, nor can we confuse duration with velocity. Of course, a developer could still confuse the initial and final velocity, but hopefully this will be more obvious. Also note that the value we get back is an Acceleration, rather than just a number.

There are, of course, many examples in other areas -- in US financial markets, stock options are traded in contracts, which represent 100 options, but their prices are quoted as single options. If you buy one contract for an option quoted at $2, you'll pay $200. Other types of options have different contract sizes -- some in the vicinity of $250,000, leading to quite a few costly mistakes over the years. Bonds are sometimes said to yield a certain percentage of interest, while at other times, traders speak of basis points -- hundredths of a percent. When developing financial software, it would certainly be beneficial if one did not have to worry about these conventions -- yields should be quantities (possibly of type Yield), and the number of options you hold should also be a quantity. Units should only come into play when values are displayed or sent to external systems. This can all be achieved using JSR-108.

Status

I managed to find several implementations, which I have listed in the references. Only one of those, the one on sourceforge, resides in javax.units and seems very limited and inactive. I would be happy to know if readers are aware of other implementations.

Limitations

JSR-108 does not require changes to the language. While this approach increases the probability of implementation, it is somewhat restrictive. For instance, it does not allow compilers to perform some possibly desirable compile-time optimizations and checks.

Also, since JSR-108 approaches the units issue from the natural sciences direction, the notion of discrete quantities seems to have been ignored. Developers in some areas may wish to define quantities or dimensions that can only take on integer values or values that are otherwise restricted.

Possibly the greatest current limitation, a side effect of the status of JSR-108, is that a lot of effort is called for to create unit databases for various fields of business and science. Such effort is only likely to be taken on by the Java community if the foundations are made more solid.

Finally, in existing implementations, units are all of instances of Unit. Subclassing is not used to indicate inheritance. As an example, we may have 100 apples and 100 oranges -- certainly not compatible units, but if we're making fruit salad, we may be interested in treating both as fruits.

Future Directions

Other than the urgent need to complete a publicly available, solid version of javax.units, some future directions can be pointed out. Of course, these are the author's personal opinions and are not endorsed by the Java community:

  • Changes to the language, not necessarily as suggested in the pseudo-code below, could dramatically improve the efficiency of using units and dimensions. Here is how our sample method would look:

    /**
     * 
     * @param initialVelocity initial velocity
     * @param finalVelocity final velocity
     * @param duration duration 
     * @return acceleration
     */
    
    public static double [m/s2] calcAcceleration(
    double initialVelocity [m/s],
    double finalVelocity [m/s], 
    double duration [s]) {
    
        if (duration <= 0) {
            throw new IllegalArgumentException(
            "Duration must be positive"
            );
        }
    
        return (finalVelocity - initialVelocity) / duration;
    }
  • Examine the relationships between dimensions and class hierarchies. Even in the simple example of apples and oranges, both derivable from fruit, difficult questions can be raised: if we wish to write a method that accepts the input of dimension weight of apple and another one that accepts weight of fruit, while a third method takes apple as input, do we need to create all of those dimensions? Is there room for a language mechanism that could assist us?

  • It would seem that some implementations could benefit greatly from the introduction of generics in JDK 1.5, saving the need for runtime casting or extra method calls. The introduction of generics, together with the suggestion for change in the language above, could lead to HashSet<Apple>.size() returning int[Apple].

References

Ori Kushnir is the founder and CTO of New-York-based June Technologies, a startup firm specializing in the development of financial models and trading technologies.


Return to ONJava.com.