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
defined.

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

square(x) x^2

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,

squirt(x) sqrt(x)

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

fahrenheit() tempF

The definition must be a nonlinear unit; for example, the synonym

fahrenheit() meter

will result in an error message when `units` starts.

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)