EiffelUnits is a library of units of measurement for theEiffel language. It enables you to attach units to the quantities you use in your programs. EiffelUnits includes a collection of common units along with conversion, formatting and consistency checking utilities. EiffelUnits is extensible and can be adapted to user-defined unit systems outside the SI (e.g. currencies, color values, ...). I only compiled it with ISE Eiffel 5.1 - if you've tried it with other compilers, please tell me.

Contents: Scope  ¤  Usage  ¤  Problems  ¤  Glossary  ¤  References


Why units?

Units of measurement are an indispensable tool in many branches of science. Our ancestor's invention of pure numbers was certainly a big step in human history. The abstraction of sets out of incommensurable goods (17 geese and 4 bottles of wine make 21 ... guess what? ... objects , of course) is often useful . But we all know that abstract classes are not enough. At times, it's recommendable to categorize values into meaningful dimensions, such that we can better track and understand what we are calculating.

Our knowledge of natural laws stems partly from a powerful technique called Dimensional Analysis. Basically, dimensioned quantities are nothing more than values attributed with a dimension. By noting these dimensions explicitly, we can check whether our equations and calculations are meaningful. All natural laws discovered so far are dimensionally consistent, which leads us to the presumption, that there really is such a thing as a `dimension' in nature. We can employ the dimensions of the quantities we use as another means of ensuring correctness in our programs because dimensionally inconsistent calculations are definitely wrong (and thus raise an exception when incommensurable units are used). Dimensioned quantities are a tool which enables you to write systems that are better contracted in contrast to systems just using plain values.

The SI (système international d'unités; International System of Units)

SI's 7 base units and 2 supplementary units

Dimension Unit
SI base units:
Length (L) m (meter)
Mass (M) kg (kilogram)
Time (T)
s (second)
Electric Current (I) A (Ampère)
Thermodynamic Temperature (Theta) K (Kelvin)
Amount of Substance (N)
mol (mole)
Luminous Intensity (J)
cd (candela)
SI supplementary units:
Plane Angle (alpha) rad (radian)
Solid Angle (Omega)
sr (steradian)

The SI is an internationally agreed system of coherent units. It declares 7 base dimensions and exactly one unit per base dimension as a base unit, the core reference of scale in that dimension. No other (obsolete) unit should be used where SI units exist. A prominent example is meter , which is the SI base unit of base dimension length (L). Inch , mile, etc. are deprecated non-SI units. Base dimensions are orthogonal, in the sense that no base dimension can be expressed as a multiplicative combination of other base dimensions. Units can be prefixed by a set of standard multipliers (e.g. centi for10-2 or mega for 106).

Derived units are constructed as combinations of base units. Force for example has a derived dimension (M * L * T-2) whose unit kg * m * s-2 may also be written as N (Newton) .

There are two additional supplementary dimensions in the SI: the dimensions of plane and solid angles. The commitee seems not to have agreed on the nature of angular units. Even though there's little reason not to get angular units in the circle of base units, they are not "official" units. The mere fact that angular dimensions are often omitted is no justification for dropping them altogether. It is highly questionable whether we should really "merge" (meaning "confuse") the two fairly different notions of angle (a dimension with base unit rad) and dimensionless ratio ( angle / angle_of_full_revolution or length_of_circle_arc / circle_radius ). Therefore, EiffelUnits treats angular units like normal base units.


Cluster "examples" shows common usage patterns of EiffelUnits. Cluster "test" contains some test classes.


Clients use the units and dimensions supplied with EiffelUnits. If your application stays within the SI unit system, it's likely that all the units you need are provided ready to use. Cluster unit.units holds classes with base dimensions and common units in these dimensions or powers thereof (e.g.m (meter) and l (liter) in class LENGTH, or s (second) and Hz (Hertz) in class TIME). Class COMPLEX_DERIVED provides some derived units with special names like N (Newton).

Class STANDARD_UNITS is a convenience class which includes all predefined units. The easiest way for clients to get access to EiffelUnits is by inheriting from STANDARD_UNITS. Another possibility (which doesn't clutter the namespace of your classes) is to define an attribute of type STANDARD_UNITS and access the units via a qualified call. Note that all classes defining units and dimensions are expanded. This allows clients to access their once-functions via an attribute without the need to create an object.

class MY_CLASS inherit
    ... -- can directly use units

class MY_CLASS
    ... -- use units via `unit'.***

The primary notion for calculations with units is class QUANTITY. Enities representing quantities are declared using type QUANTITY:

altitude: QUANTITY

A QUANTITY object contains a DOUBLE value and a dimensionality. Quantities with equal dimensionality are said to be commensurate. Only commensurate quantities can be added, subtracted and compared to each other. These commensurateness conditions can be stated in contracts as shown here:

    increase_altitude (by: QUANTITY) is
            by.commensurate_with (m)

    altitude.commensurate_with (m)

QUANTITY is an expanded class whose entities can hold any unit or quantity. Yes, that's right: units are not special objects. They are just quantities which happen to carry some dimension. You usually don't have to "create" a unit for your quantity from scratch. Instead, you use once functions from classes in cluster unit.units and employ them as factors in multiplications and divisions with other quantity objects. Class QUANTITY_CONVERSION(which is an ancestor of STANDARD_UNITS, but can also be used directly) provides feature quantity (value: DOUBLE): QUANTITY to create dimensionless quantities:

    lightspeed: QUANTITY is
            Result := quantity (299792458) * m / s

Temperatures in units other than Kelvin require special treatment. Instructions are given in class THERMODYNAMIC_TEMPERATURE.

Commensurability between quantities is defined as equality of their dimensions. Therefore, every QUANTITY has a DIMENSION attached, which is accessible through feature dimension. Class STANDARD_DIMENSIONS holds a set of commonly used dimensions, including the SI base dimensions. An instance of class STANDARD_DIMENSIONS is available via feature dim from class QUANTITY_CONVERSION, which you will most likely inherit if you intend to use EiffelUnits.

Picture of Atwood's MachineCase Study: Atwood's Machine

(This example is taken from [1].) Atwood's Machine is a simple mechanical device which consists of two unequal masses suspended by a string from a massless, frictionless pulley. The upward acceleration a of mass m1is given by

a = (m2 - m1) / (m2 + m1)

and the tension t in the string is given by

t = 2 * g * m1 * m2 / (m1 + m2)

where g is the magnitude of the acceleration due to gravity. These simple formulas are incorporated into the following class for Atwood's machine:


feature {NONE} -- Initialization

    make (mass_1_: QUANTITY; mass_2_: QUANTITY) is
            -- Load the machine
            is_mass_1: mass_1_.commensurate_with (kg)
            is_mass_2: mass_2_.commensurate_with (kg)
            mass_1 := mass_1_
            mass_2 := mass_2_

feature -- Access

    mass_1: QUANTITY
    mass_2: QUANTITY
    acceleration_1: QUANTITY is
            -- acceleration of mass 1
            Result := (mass_2 - mass_1) / (mass_1 + mass_2) * Gravity
            Result.commensurate_with (m * s^-2)

    tension: QUANTITY is
            -- tension in the string
            Result := quantity (2) * (mass_1 * mass_2) / (mass_1 + mass_2) * Gravity
            Result.has_dimension (dim.Force)

feature -- Constants

    Gravity: QUANTITY is
            -- gravitational acceleration on earth surface
            Result := quantity (9.81) * m / (s ^ 2)
            Result.has_dimension (dim.Acceleration)

    mass_1: mass_1.commensurate_with (kg)
    mass_2: mass_2.has_dimension (dim.Mass)

end -- class ATWOODS_MACHINE


If you need a derived dimension which is not defined in class STANDARD_DIMENSIONS, you can easily create it by multiplying and dividing base dimensions or existing derived dimensions. Remember that derived dimensions do not have to be "created" prior usage. They are generated on-the-fly as you execute calculations with existing units.

Sometimes, you need a new dimension for your special field of application. In this case, you simply create a new DIMENSION object, which becomes a new base dimension (orthogonal to all previous dimensions):

class MY_CLASS inherit


    My_dimension: DIMENSION is
            create Result.make_new_base_dimension
    my_base_unit: QUANTITY is
            Result := base_quantity (My_dimension)

New Units: Case Study "Astronomical Units"

See class LENGTH_ASTRONOMY in cluster root_cluster.examples to see how you could provide additional units or constants.

New Dimensions: Case Study "Fuel Consumption"

This example shows how to extend EiffelUnits. New base dimensions Gasoline and Distance are created to aid in consistency checks. New units l_gasoline and m_distance are defined on top of the new dimensions.

expanded class

feature -- Base Dimensions
    Gasoline: DIMENSION is
            create Result.make_new_base_dimension
    Gasoline_volume: DIMENSION is
            Result := Gasoline * dim.Volume
    Distance: DIMENSION is
            create Result.make_new_base_dimension
            Result := Result * dim.Length
    Fuel_consumption: DIMENSION is
            Result := Gasoline_volume / Distance

feature -- Units
    l_gasoline: QUANTITY is
            Result := base_quantity (Gasoline) * l
            definition: Result.is_equal (base_quantity (Gasoline) * l)
            Result.has_dimension (Gasoline_volume)
    km_distance: QUANTITY is
            Result := quantity (1000) * base_quantity (Distance)
            definition: Result.is_equal (quantity (1000) * base_quantity (Distance))
            Result.has_dimension (Distance)

    description: "A Car with an average fuel consumption"

class CAR inherit


feature -- Initialization

    make (consumption_: QUANTITY) is
            -- Set fuel consumption to `consumption_'
            consumption_.has_dimension (Fuel_consumption)
            consumption := consumption_
            consumption_set: consumption = consumption_

feature -- Access
    km_for_tank (tank_contents: QUANTITY): QUANTITY is
            -- how far can current car go?
            tank_contents.commensurate_with (l_gasoline)
            Result := tank_contents / consumption
            Result.commensurate_with (km_distance)

feature -- Status report

    consumption: QUANTITY
        -- average fuel consumption

    consumption.has_dimension (Fuel_consumption)

end -- class CAR


Units are not statically checked. Unfortunately it was not possible to implement unit checking statically (at compile time). In complex unit expressions, we have to deal with a great many unforeseeable compound units. It is not feasible to manually provide classes and operations for all possible combinations of units. Furthermore, that approach would not be extensible.
In Eiffel, types are based either directly on classes or on generic derivations of generic classes. Since types are not computable on demand at compile time, I don't see a possibility to implement static unit checking. Please inform me if you disagree and have a solution. But remember: we don't want preprocessors or compiler hacks. Just a clean Eiffel library.
The increased safety gained with unit checking is currently traded in for a runtime overhead over unchecked programs.

Persistence of quantities is not supported. Base dimensions are currently identified in once functions by a monotonous increasing counter. Since the execution sequence of once functions is not neccessarily the same in different sessions, dimensions are not guaranteed to be assigned the same identifiers between runs. Therefore, quantity objects can not be stored and retrieved safely between different executions of a program by means of STORABLE.
A possible solution: identify a base dimension by its symbol (e.g. "L" for length), stored in some global place. But that would not statically ensure uniqueness.

EiffelUnits is based on ISE EiffelBase. I apologize for any inconveniences in terms of portability.

All values are DOUBLE. EiffelBase has weak support for generic usage of numeric data types. INTEGER and DOUBLE both inherit independently from NUMERIC and from COMPARABLE. Hence there's no common ancestor that clients can use if they don't care about the exact data type (be that INTEGER, DOUBLE, RATIONAL, BIG_INTEGER, etc.). The following class declaration is not legal in Eiffel: class QUANTITY [G -> {NUMERIC, COMPARABLE}] inherit ...

Conversion of DOUBLE value to QUANTITY. At the time of writing, there's no automatic object conversion in Eiffel. Therefore, you have to use feature quantity (v: DOUBLE): QUANTITY from classQUANTITY_CONVERSION to make the transition from (manifest) numbers to quantities. The reverse way is feature in (unit: QUANTITY): DOUBLE from class QUANTITY. 

Known Bugs. There are no known bugs at this time. If you find one, please crunch it and send it to me. Thank you.


Base dimensions:
A set of dimensions (physical properties) which are assumed to be mutually independent. A base dimension can not be expressed by any combination of the other base dimensions. The base dimensions of the SI are listed in the table above.
Derived dimension:
A dimension that results from a product or quotient of two or more base dimensions. "Derived" is the nomenclature specified by the SI.
A measurable or calculable physical property, for example energy. SI uses the term "quantity" for this concept. A dimension may be a base dimension or a derived dimension.

Quantity (dimensioned quantity):
A numeric value together with its dimension.

A Quantity which defines a reference of scale for measurements in a certain dimension. SI specifies one base unit for each base dimension. Certain derived dimensions have a base unit too (e.g. dimension force has base unit N, which stands for m * kg * s-2).


[1] John J. Barton and Lee R. Nackman: "Scientific and engineering C++: an introduction with advanced techniques and examples". Reading, Massachusetts, Addison-Wesley, 1994.

Further information on units of measurement and standards can befound here:

Copyright ©2002 Markus Keller, markus_keller@student.ethz.ch
This library is free for use and redistribution by everybody.
It resulted from a semester project at ETH Zürich ,Chair of Software Engineering
Last change: 2002-10-08