SI Units
Checked and Unchecked

Christoph Grein

Original Version 9 September 2002
Last Update 14 December 2011



In From The Big Bang To The Universe, I have shown that Ada is not suited tovimplementing physical dimensions with its type concept. Therefore a method was presented applicable also to hard real-time systems which gives up dimension checking within the SI units system, while keeping the advantages of strong typing in critical cases when units from different systems have to be mixed. And indeed, this method has been in use in several avionics projects for more than a decade.

But the request to have full dimensional checking persists as can be seen from the pertinent discussions in newsgroup Comp.Lang.Ada. There are two basic ways one can try to solve the problem, either by adding to the numeric value its dimension as attribute, or by using different types for different dimensions. Since the first method is runtime-consuming, whereas the second one is only compiletime-consuming, all effort naturally concentrates on the second method. This is doomed to fail as is shown in the paper cited above - although a plethora of overloaded operations is used, the result is not really satisfactory. Physical equations with all their powers and roots evade these attempts.

So here a different method is presented to deal with physical items in full generality by adding to the value its dimension as an attribute, i.e. the first way rejected above because of its runtime-expensiveness. Hence this method might appear prohibitive for use in hard real-time systems. This is not the case!

By changing only a few lines of code, dimensions can be switched off, leaving the program, however big it may be, with only the pure numerics.

The method comes in two variants, unconstrained and constrained:

Both variants differ only in the declaration of objects; expressions and assignments look the same. They are very easy to use by offering a natural notation for dimensioned objects as is shown by a whole lot of sample (test) programs.

A subdirectory of the download holds the Original_Paper and another one the complete Documentation. Test programs for all packages can be found in the subdirectory Test.

1. Unconstrained

The first variant uses the type declaration

  type Item is private;

where the full declaration composes the physical item of dimension and value, where the values for the SI base units Meter, Kilogram, Second, Ampere, Kelvin, Candela, Mole are rational numbers representing the exponent (e.g. m2). We have to use rational numbers because we want to be able to represent the most general physical equations.

  type Dimension is record
    m, kg, s, A, K, cd, mol: Rational;
  end record;

  type Item is record
    Unit : Dimension;
    Value: Real;
  end record;

Two implementations are provided: The package hierarchy


provides operations for dealing with checked dimensions (the last two packages are only proposals for further additions and do not appear in the other variants).

The package Unconstrained_Checked_SI defines the private SI base type Item as shown above and all operations on it, including mathematical functions. Several children are defined:

Since the type Item is private, no subtypes with range constraints can be defined. The child package Generic_Item_Subtype provides a substitute. It allows to define a new type Constrained with a range constraint and provides operations that make Constrained completely compatible with Item so that virtually there is no difference to using Ada subtypes.

The child package Generic_Units defines all derived units like Newton and Tesla.

The child package Generic_Text_IO defines facilities like those in Ada.Text_IO.Float_IO, but includes the dimension in symbolic form (e.g. 1.0*km/h for speed or 2.3*N*m for torque). Like the Ada prefined input operations, the corresponding operations leave the character that stopped reading in the input stream when Data_Error is raised. By default, all SI unit symbols may be used; additionally arbitrary symbols (like e.g. ft) may be defined.

The child packages Generic_Temperatures and Generic_Temperatures.Generic_Text_IO define the temperature scales Celsius, Fahrenheit, Rankine, and Reaumur and the correpsonding dimensioned IO operations.

The child package Generic_Polynomial_Numerics defines operations for polynomials, linear interpolation and second order curve approximation.

The child package Generic_Vector_Space and its child Generic_Transformation defines operations for 3-dimensional vectors (inner and cross products, matrix multiplication, rotations). Neither the inner nor the cross product of vectors are defined as operators to avoid ambiguities. The procedure Ambiguities in the subdirectory Examples shows the kind of ambiguities that would be present if they were defined as operators. This package is not a linear algebraics package. However, Ada 2005 adds such a package to the RM in the numerics annex, chapter G.3.1, called Ada.Numerics.Generic_Real_Arrays. In order to ease use of the additional functionality provided with this new package, SI arrays and vectors are now defined via its types Real_Vector and Real_Matrix. The procedure Vectors in the subdirectory Examples shows how to use the functionality of Generic_Real_Arrays. [Since the numerics annex is not part of the core language and thus need not be implemented by every compiler, the previous definition of SI arrays and vectors not using the annex has been kept in subdirectory No_Chapter_G. So if your compiler does not implement chapter G, just replace the packages in the corresponding directories by those. You also have to adapt a few test programs in this case; your compiler will tell you where.]

The child package Generic_Quaternion_Space and its child Generic_Transformation were supplied by Chris Holmes. Quaternions are an extension of complex numbers. They can be used for vector rotational transformations as a replacement of a multiplication by an orthogonal matrix (with them, the transformation formulae obtain an especially simple form). Examples how to do this are given by the procedures Quaternion_Transformation and Rotation in the subdirectory Examples. The latter example shows however that matrix rotation is faster than quaternion rotation by a factor of about 5.

The child packages Generic_Natural_Constants and Generic_Non_SI_Units are only sketched as a proposal where to put natural constants (like the speed of light) and other units not derived from SI units (like knots). Natural constants are subject to changes with increasing measurement accuracy (with the exception of the speed of light in vacuum, which is exact by definition, and a few others), so they have to be defined with their current values at the time of a project's start; and normally only a few of them are needed anyway. With other units, things are similar: There is such a plethora of units all over the world that a project has to choose which of them are actually needed.

These packages can very easily be replaced by the corresponding ones in the parallel hierarchy


which have exactly the same visible specifications, but replace the full declaration of items by only its value:

  type Item is record
    Value: Real;
  end record;

The program Physical_Computations_Unconstrained in the subdirectory Examples shows how this is done. You really only have to change at most three lines of code, however big your whole program may be.

While this variant provides full internal consistency, an object's dimension may change:

  Dist: Item := 5.0 * Meter;
  Dist := Dist / (2.0 * Second);

Now Dist, contrary to its name, has the dimension [m/s]. This is why this variant is called the unconstrained variant.

2. Constrained

In the constrained variant, the declaration of Item adds an unknown discriminant

  type Item (<>) is private;

The full declaration uses the unconstrained item as above and adds numerator and denominator of each basic unit as discriminants:

  package SI is new Unconstrained_Checked_SI (Real);

  type Item (m_N, kg_N, s_N, A_N, K_N, cd_N, mol_N: Whole;
             m_D, kg_D, s_D, A_D, K_D, cd_D, mol_D: Positive_Whole) is record
    Inner: SI.Item;
  end record;

This prevents changes of dimension after declaration; assignments like the one above with Dist will inevitably raise an exception.

The two package hierarchies are (not all units present in the unconstrained hierarchy are available as yet)




The program Physical_Computations_Constrained in the subdirectory Examples shows how this package is used. Again exchange of checked and unchecked is done with the same few changes as above.

While this variant prevents changes of dimension, its major drawback is that arrays of items cannot be defined:

  type Vector is array (Natural range <>) of Item;  -- illegal

This is why the child package Generic_Polynomial_Numerics has a specification different from the unconstrained one. The program Test_Constrained_Polynomial_Numerics in subdirectory Test shows how to use it. Also for Generic_Vector_Space, something special has been invented, again using unknown discriminants.

Also reading data from files is more complicated since the dimension of the item to be read has to be known beforehand. Actually, this should not present any problems because this is normally the case. However for the rare cases where items of unknown dimension have to be read, functional forms are additionally defined in Generic_Text_IO for the Get subprograms.

3. Conversion between Constrained and Unconstrained Items

Actually these variants are considered alternatives, where just one and only one of them should be chosen for an application. Since however composition of constrained items is problematic, you might wish to use unconstrained items for composite objects and constrained ones otherwise. Therefore the generic packages


have been created enabling conversion between items of both variants.

Subdirectory Elaborated_Example holds an example of how to use this conversion facility. This example also shows in detail, which lines have to be changed when switching from checked to unchecked items. You can see that indeed it is only a handful, irrespective of the size of the application proper.

4. Execution Time Measurements

There is a program to measure the computation speeds in subdirectory Examples. The relative results vary wildly because the measurements were made on timesharing systems; some are as follows:

Constrained Checked 15.8   9.3   12.0
Unchecked 1.2   1.1   1.0
Unconstrained Checked 9.8   6.7   7.2
Unchecked 1.0   1.0   1.0

These figures are for the full set of SI base units. The packages are easily customizable by commenting out the less frequently used units (Mole, Candela, Kelvin), which should increase the speed accordingly.

5. Licencing

The software is published under the GNAT Modified GPL, see file gpl.txt and the header of each compilation unit.

6. Download

Ada 2005

Download from here the Ada 2005 version.

Since the code relies on heavy overloading for dealing with rational exponents, see here the known behaviour of some Ada compilers:

Since both, GNAT GPL 2005 and GNAT Pro 6.0.1, do not include the linear algebraics library, leading to link errors when vectors are used, a version without chapter G has been provided. It does however not include all the examples as the full version.
Download the Ada 2005 version without RM Chapter G support, so you do not need to perform the replacements mentioned above.

Ada 95

If you don't have an able Ada 2005 compiler, download the Ada 95 version of 27 June 2005. If you have however GNAT in a version before 3.16a (never gone public; the last public version was 3.15p), you need some work-arounds for compiler bugs like qualified expressions Rational'(-2/3) instead of the simple (rational) expression -2/3 (the compiler will tell you where; 3.15p has some more quirks).
Note that the free GNAT GPL 2005 and the supported GNAT Pro 5.03a and GNAT Pro 6.0.1 work correctly with this version. So it's high time that you updated your old compiler.

7. First Publication

This work has been published in Softwaretechnik-Trends Vol. 22.4 (November 2002), the periodical of Ada-Deutschland, special interest group 2.1.5 Ada within Gesellschaft für Informatik, the German Informatics Society.

It has also been presented at the Ada Europe Conference 2003 in Toulouse.


A C++ solution (it does however not include fractional powers) with templates was presented at Currently it's only mentioned at the end of the page, but the link is broken. The big difference is that C++ templates with their implicit instantiations allow type checking during compile-time, so that no overhead neither in memory space nor in run-time is incurred. In this respect, C++ templates are more powerful than Ada generics. However at the time when the code was still there (11 February 2004), not all C++ compilers, although capable of compiling the code, were able to actually perform this type checking (as stated in the documentation of the method).

All about SI units and general information about the foundation of modern science and technology can be found at the National Institute of Standards and Technology.

Origin of prefix names
Name Symbol value derived from meaning
deka da 1e+01 deka (Greek) ten
hecto h 1e+02 hekaton (Greek) hundred
kilo k 1e+03 chilioi (Greek) thousand
mega M 1e+06 megas (Greek) large
giga G 1e+09 gigas (Greek) giant
tera T 1e+12 teras (Greek) monster
peta P 1e+15 pente (Greek) five
exa E 1e+18 hexa (Greek) six
zetta Z 1e+21 septem (Latin) seven
yotta Y 1e+24 octo (Latin, Greek) eight
deci d 1e-01 decimus (Latin) tenth
centi c 1e-02 centum (Latin) hundred
milli m 1e-03 mille (Latin) thousand
micro µ 1e-06 micro (Latin)
mikros (Greek)
nano n 1e-09 nanus (Latin)
nanos (Greek)
pico p 1e-12 pico (Spanish) bit
femto f 1e-15 femten (Danish, Norwegian) fifteen
atto a 1e-18 atten (Danish, Norwegian) eighteen
zepto z 1e-21 septem (Latin) seven
yocto y 1e-24 octo (Latin, Greek) eight

u is normally used where the Greek character µ is not available.
Also note that all symbols, prefixes and units, are case-sensitive.

Origin of unit names
Name Symbol derived from
candela cd candela (Latin) candle
gram g gramma (a Greek mass unit)
lumen lm lumen (Latin) light
lux lx lux (Latin) light
meter m metrum (Latin), metron (Greek) measure
mole mol molecule
radian rad ?
second s secunda pars (Latin) second part

The symbols sec and S, often seen, are wrong.

Name Symbol named in honor of
ampere A André-Marie Ampère (1775-1836) France
becquerel Bq Antoine-Henri Becquerel (1852-1908) France
coulomb C Charles-Augustin de Coulomb (1736-1806) France
farad F Michael Faraday (1791-1867) England
gray Gy Louis Harold Gray, F.R.S. (1905-1965) England
henry H Joseph Henry (1797-1878) United States
hertz Hz Heinrich Rudolf Hertz (1857-1894) Germany
joule J James Prescott Joule (1818-1889) England
kelvin K William Thomson, Lord Kelvin (1824-1907) England
newton N Sir Isaac Newton (1642-1727) England
ohm Ω Simon Ohm (1787-1854) Germany
pascal Pa Blaise Pascal (1623-1662) France
siemens S Ernst Werner von Siemens (1816-1892) or his brother
Sir William (Karl Wilhelm von) Siemens (1823-1883) Germany (England)
sievert Sv Rolf Maximilian Sievert (1896-1966) Sweden
tesla T Nikola Tesla (1856-1943) Croatia (United States)
volt V Count Alessandro Volta (1745-1827) Italy
watt W James Watt (1736-1819) Scotland
weber Wb Wilhelm Eduard Weber (1804-1891) Germany

A: Note that the unit ampere is written without a diacritical mark.
Ω: I do not know a common replacement for the Capital Greek Omega.
S: In older literature also the symbol mho is in use.

Updates Reason
14 December 2011 In variant Unconstrained_Checked_SI, type Dimension and functions Dimension_of/as moved to subpackage for_Test_only in order to make intention clear.
22 June 2010 Bug fix: cd=lm/sr.
30 May 2009 GNAT GPL 2009 finally compiles without bugs.
3 March 2009 GNAT Pro 6.2.1 finally compiles without bugs.
4 August 2008 Text_IO now allows definition of arbitrary unit symbols.
6 May 2008 Minor update in Get.
28 April 2008 Added IO with strings for the constrained variants.
Also added Width parameter to Get from file for all variants.
19 April 2008 New GNAT Pro 6.1.1 has some bugs fixed.
5 Nov 2007 Very minor improvement in documentation.
18 Jun 2007 GNAT GPL 2007 can compile the Ada 2005 version (with caveat).
Provide also a no RM chapter G version.
15 Mar 2007 GNAT 6.0.1 by mistake does not include libgnalasup.a. Prepared GNAT project files for work-around.
Un/Constrained_Checked_SI.Generic_Item_Subtype made Pure again by a simple code change.
Bug fix: had the wrong Ada name.
6 March 2007 Changed the deprecated -gnatN to -gnatn in some project files.
Removed the work-around introduced on 5 March (does not occur with -gnatn).
5 March 2007 Un/Constrained_Checked_SI.Generic_Item_Subtype cannot be Pure. (The unchecked variants can be Pure.)
Bug fix in Constrained_Checked_SI.Generic_Item_Subtype: The wrong instance of Unit_Error was raised.
14 April 2006 Generic_Vector_Space.Generic_Transformation exists now also for the constrained variant.
Conversion between constrained and unconstrained Vector_Space.
Directories have been restructured.
29 Mar 2006 Another important update:
Vectors and matrices are now defined via the RM numerics annex G.3.1.
The previous definition is kept as an alternative if the compiler does not implement the annex.
1 Mar 2006 Important update:
A conversion facility between constrained and unconstrained items has been added.
The pragma Pure has been added where applicable.
18 Feb 2006 A detailed documentation for each package added.
Polar coordinates are now also private, i.e. well implemented. Bug fix in Rational_Arithmetics.Value.
Test programs added.
24 Nov 2005 The Ada 2005 version is new:
Because Ada 2005 undefines non-dispatching abstract operations, the package Invisible could be removed.
In Ada 95, these operations were still visible, and thus the use-type-clause for the type Whole was disallowed because it would have made unwanted operations hidden in Invisible directly visible again.
The Ada 95 version (27 June 2005) is still available:
27 Jun 2005 Last Ada 95 version, no longer maintained. Link to homepage corrected.
25 Jun 2005 Measured speed quaternion vs. matrix rotation.
8 Sep 2004 Minor change for consistency reasons: Renamed function Unit in package Quaternion_Space to Normalize to be consistent with naming in package Vector_Space.
6 Sep 2004 Origin of unit Sievert. Quaternion rotation about given axis added; test programs and examples improved.
14 May 2004 Note about seeming invisibility added.
12 May 2004 The quaternion rotation package has been added for the unconstrained variant.
8 May 2004 Vectors and matrices now are private even for the unconstrained variant, but have prototypes like in the constrained variant.
A matrix rotation package has been added for the unconstrained variant.
A quaternion package has been added for the unconstrained variant.
26 Feb 2004 Vector and matrix prototypes also for unconstrained variant.
Reference to C++ solution and list of prefix and unit name origins added.
12 Mar 2003 IO customization improved.
26 Feb 2003 Generic_Polynomial_Numerics in the constrained family is new.
With the advent of GNAT 3.16a, the last work-arounds could be removed except one in program Measure in subdirectory Examples.
4 Feb 2003 Syntax for reading unit symbols changed; it now resembles the reading of enumeration literals. Only input raising Data_Error is treated differently.
20 Dec 2002 Added subtyping capability for all variants, vector arithmetics for the unconstrained variant.
Added the volume of Softwaretechnik-Trends in which this work was published.
21 Oct 2002 XHTML successfully validated with W3C validation facility. No functional change.
14 Oct 2002 Graphic files were missing in zip file.
11 Oct 2002 Temperature scales Celsius, Fahrenheit, Rankine, Réaumur added.
27 Sep 2002 Some of the work-arounds for a GNAT 3.14p bug could be removed with the arrival of 3.16w.
15 Sep 2002 Added unit Katal and a link to the NIST.
The argument of trigonometric functions with cycle parameter can be dimensioned.
9 Sep 2002 First release.

English Home Contents
Deutsch Inhaltsverzeichnis
welcome homepage

Valid XHTML 1.0 Transitional!