Some unit conversions of interest are nonlinear; for example, temperature conversions between the Fahrenheit and Celsius scales cannot be done by simply multiplying by conversion factors.
When you give a linear unit definition such as ‘inch 2.54 cm’
you are providing information that
units uses to convert
values in inches into primitive units of meters. For nonlinear units,
you give a functional definition that provides the same information.
Nonlinear units are represented using a functional notation. It is best to regard this notation not as a function call but as a way of adding units to a number, much the same way that writing a linear unit name after a number adds units to that number. Internally, nonlinear units are defined by a pair of functions that convert to and from linear units in the database, so that an eventual conversion to primitive units is possible.
Here is an example nonlinear unit definition:
tempF(x) units=[1;K] domain=[-459.67,) range=[0,) \ (x+(-32)) degF + stdtemp ; (tempF+(-stdtemp))/degF + 32
A nonlinear unit definition comprises a unit name, a formal parameter
name, two functions, and optional specifications for units, the domain,
and the range (the domain of the inverse function). The functions tell
units how to convert to and from the new unit. To produce
valid results, the arguments of these functions need to have the correct
dimensions and be within the domains for which the functions are
The definition begins with the unit name followed immediately (with no
spaces) by a ‘(’ character. In the parentheses is the name of the
formal parameter. Next is an optional specification of the units
required by the functions in the definition. In the example above,
the ‘units=[1;K]’ specification indicates that the
‘tempF’ function requires an input argument conformable with
‘1’ (i.e., the argument is dimensionless), and that the inverse
function requires an input argument conformable with ‘K’. For
normal nonlinear units definition, the forward function will always take
a dimensionless argument; in general, the inverse function will need
units that match the quantity measured by your nonlinear unit.
Specifying the units enables
units to perform error checking
on function arguments, and also to assign units to domain and range
specifications, which are described later.
Next the function definitions appear. In the example above, the ‘tempF’ function is defined by
tempF(x) = (x+(-32)) degF + stdtemp
This gives a rule for converting ‘x’ in the units ‘tempF’ to linear units of absolute temperature, which makes it possible to convert from tempF to other units.
To enable conversions to Fahrenheit, you must give a rule for the inverse conversions. The inverse will be ‘x(tempF)’ and its definition appears after a ‘;’ character. In our example, the inverse is
x(tempF) = (tempF+(-stdtemp))/degF + 32
This inverse definition takes an absolute temperature as its argument and converts it to the Fahrenheit temperature. The inverse can be omitted by leaving out the ‘;’ character and the inverse definition, but then conversions to the unit will not be possible. If the inverse definition is omitted, the --check option will display a warning. It is up to you to calculate and enter the correct inverse function to obtain proper conversions; the --check option tests the inverse at one point and prints an error if it is not valid there, but this is not a guarantee that your inverse is correct.
With some definitions, the units may vary. For example, the definition
can have any arbitrary units, and can also take dimensionless arguments. In such a case, you should not specify units. If a definition takes a root of its arguments, the definition is valid only for units that yield such a root. For example,
is valid for a dimensionless argument, and for arguments with even powers of units.
Some definitions may not be valid for all real numbers. In such cases,
units can handle errors better if you specify an appropriate
domain and range. You specify the domain and range as shown below:
baume(d) units=[1;g/cm^3] domain=[0,130.5] range=[1,10] \ (145/(145-d)) g/cm^3 ; (baume+-g/cm^3) 145 / baume
In this example the domain is specified after ‘domain=’ with the endpoints given in brackets. In accord with mathematical convention, square brackets indicate a closed interval (one that includes its endpoints), and parentheses indicate an open interval (one that does not include its endpoints). An interval can be open or closed on one or both ends; an interval that is unbounded on either end is indicated by omitting the limit on that end. For example, a quantity to which decibel (dB) is applied may have any value greater than zero, so the range is indicated by ‘(0,)’:
decibel(x) units=[1;1] range=(0,) 10^(x/10); 10 log(decibel)
If the domain or range is given, the second endpoint must be greater than the first.
The domain and range specifications can appear independently and in any
order along with the units specification.
The values for the domain and range endpoints are attached to the units
given in the units specification, and if necessary, the parameter value
is adjusted for comparison with the endpoints. For example, if a
definition includes ‘units=[1;ft]’ and ‘range=[3,)’, the range
will be taken as 3 ft to infinity. If the function is passed a
parameter of ‘900 mm’, that value will be adjusted to
2.9527559 ft, which is outside the specified range.
If you omit the units specification from the previous example,
units can not tell whether you intend the lower endpoint
to be 3 ft or 3 microfurlongs, and can not adjust the
parameter value of 900 mm for comparison. Without units,
numerical values other than zero or plus or minus infinity for domain or
range endpoints are meaningless, and accordingly they are not allowed. If
you give other values without units then the definition will be ignored
and you will get an error message.
Although the units, domain, and range specifications are optional, it’s
best to give them when they are applicable; doing so allows
units to perform better error checking and give more helpful
error messages. Giving the domain and range also enables the
--check option to find a point in the domain to use for its
point check of your inverse definition.
You can make synonyms for nonlinear units by providing both the forward and inverse functions; inverse functions can be obtained using the ‘~’ operator. So to create a synonym for ‘tempF’ you could write
fahrenheit(x) units=[1;K] tempF(x); ~tempF(fahrenheit)
This is useful for creating a nonlinear unit definition that differs slightly from an existing definition without having to repeat the original functions. For example,
dBW(x) units=[1;W] range=[0,) dB(x) W ; ~dB(dBW/W)
If you wish a synonym to refer to an existing nonlinear unit without modification, you can do so more simply by adding the synonym with appended parentheses as a new unit, with the existing nonlinear unit—without parentheses—as the definition. So to create a synonym for ‘tempF’ you could write
The definition must be a nonlinear unit; for example, the synonym
will result in an error message when
You may occasionally wish to define a function that operates on units. This can be done using a nonlinear unit definition. For example, the definition below provides conversion between radius and the area of a circle. This definition requires a length as input and produces an area as output, as indicated by the ‘units=’ specification. Specifying the range as the nonnegative numbers can prevent cryptic error messages.
circlearea(r) units=[m;m^2] range=[0,) pi r^2 ; sqrt(circlearea/pi)