[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

# 3. Ordinary number types

CLN implements the following class hierarchy:

                            Number
                          cl_number
                        <cln/number.h>
                              |
                              |
                     Real or complex number
                            cl_N
                        <cln/complex.h>
                              |
                              |
                         Real number
                            cl_R
                         <cln/real.h>
                              |
          +-------------------+-------------------+
          |                                       |
    Rational number                     Floating-point number
        cl_RA                                   cl_F
    <cln/rational.h>                         <cln/float.h>
          |                                       |
          |                +--------------+--------------+--------------+
       Integer             |              |              |              |
        cl_I          Short-Float    Single-Float   Double-Float    Long-Float
    <cln/integer.h>      cl_SF          cl_FF          cl_DF          cl_LF
                     <cln/sfloat.h> <cln/ffloat.h> <cln/dfloat.h> <cln/lfloat.h>

The base class `cl_number` is an abstract base class. It is not useful to declare a variable of this type except if you want to completely disable compile-time type checking and use run-time type checking instead.

The class `cl_N` comprises real and complex numbers. There is no special class for complex numbers since complex numbers with imaginary part `0` are automatically converted to real numbers.

The class `cl_R` comprises real numbers of different kinds. It is an abstract class.

The class `cl_RA` comprises exact real numbers: rational numbers, including integers. There is no special class for non-integral rational numbers since rational numbers with denominator `1` are automatically converted to integers.

The class `cl_F` implements floating-point approximations to real numbers. It is an abstract class.

> 3.1 Exact numbers |  |
> 3.2 Floating-point numbers |  |
> 3.3 Complex numbers |  |
> 3.4 Conversions |  |

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 3.1 Exact numbers

Some numbers are represented as exact numbers: there is no loss of information when such a number is converted from its mathematical value to its internal representation. On exact numbers, the elementary operations (`+`, `-`, `*`, `/`, comparisons, ...) compute the completely correct result.

In CLN, the exact numbers are:

- rational numbers (including integers),
- complex numbers whose real and imaginary parts are both rational numbers.

Rational numbers are always normalized to the form `numerator/denominator` where the numerator and denominator are coprime integers and the denominator is positive. If the resulting denominator is `1`, the rational number is converted to an integer.

Small integers (typically in the range `-2^29`...`2^29-1`, for 32-bit machines) are especially efficient, because they consume no heap allocation. Otherwise the distinction between these immediate integers (called "fixnums") and heap allocated integers (called "bignums") is completely transparent.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 3.2 Floating-point numbers

Not all real numbers can be represented exactly. (There is an easy mathematical proof for this: Only a countable set of numbers can be stored exactly in a computer, even if one assumes that it has unlimited storage. But there are uncountably many real numbers.) So some approximation is needed. CLN implements ordinary floating-point numbers, with mantissa and exponent.

The elementary operations (`+`, `-`, `*`, `/`, ...) only return approximate results. For example, the value of the expression `(cl_F) 0.3 + (cl_F) 0.4` prints as ``0.70000005`', not as ``0.7`'. Rounding errors like this one are inevitable when computing with floating-point numbers.

Nevertheless, CLN rounds the floating-point results of the operations `+`, `-`, `*`, `/`, `sqrt` according to the "round-to-even" rule: It first computes the exact mathematical result and then returns the floating-point number which is nearest to this. If two floating-point numbers are equally distant from the ideal result, the one with a `0` in its least significant mantissa bit is chosen.

Similarly, testing floating point numbers for equality ``x == y`' is gambling with random errors. Better check for ``abs(x - y) < epsilon`' for some well-chosen `epsilon`.

Floating point numbers come in four flavors:

- Short floats, type `cl_SF`. They have 1 sign bit, 8 exponent bits (including the exponent's sign), and 17 mantissa bits (including the "hidden" bit). They don't consume heap allocation.

- Single floats, type `cl_FF`. They have 1 sign bit, 8 exponent bits (including the exponent's sign), and 24 mantissa bits (including the "hidden" bit). In CLN, they are represented as IEEE single-precision floating point numbers. This corresponds closely to the C/C++ type ``float`'.

- Double floats, type `cl_DF`. They have 1 sign bit, 11 exponent bits (including the exponent's sign), and 53 mantissa bits (including the "hidden" bit). In CLN, they are represented as IEEE double-precision floating point numbers. This corresponds closely to the C/C++ type ``double`'.

- Long floats, type `cl_LF`. They have 1 sign bit, 32 exponent bits (including the exponent's sign), and n mantissa bits (including the "hidden" bit), where n >= 64. The precision of a long float is unlimited, but once created, a long float has a fixed precision. (No "lazy recomputation".)

Of course, computations with long floats are more expensive than those with smaller floating-point formats.

CLN does not implement features like NaNs, denormalized numbers and gradual underflow. If the exponent range of some floating-point type is too limited for your application, choose another floating-point type with larger exponent range.

As a user of CLN, you can forget about the differences between the four floating-point types and just declare all your floating-point variables as being of type `cl_F`. This has the advantage that when you change the precision of some computation (say, from `cl_DF` to `cl_LF`), you don't have to change the code, only the precision of the initial values. Also, many transcendental functions have been declared as returning a `cl_F` when the argument is a `cl_F`, but such declarations are missing for the types `cl_SF`, `cl_FF`, `cl_DF`, `cl_LF`. (Such declarations would be wrong if the floating point contagion rule happened to change in the future.)

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 3.3 Complex numbers

Complex numbers, as implemented by the class `cl_N`, have a real part and an imaginary part, both real numbers. A complex number whose imaginary part is the exact number `0` is automatically converted to a real number.

Complex numbers can arise from real numbers alone, for example through application of `sqrt` or transcendental functions.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 3.4 Conversions

Conversions from any class to any its superclasses ("base classes" in C++ terminology) is done automatically.

Conversions from the C built-in types ``long`' and ``unsigned long`' are provided for the classes `cl_I`, `cl_RA`, `cl_R`, `cl_N` and `cl_number`.

Conversions from the C built-in types ``int`' and ``unsigned int`' are provided for the classes `cl_I`, `cl_RA`, `cl_R`, `cl_N` and `cl_number`. However, these conversions emphasize efficiency. Their range is therefore limited:

- The conversion from ``int`' works only if the argument is < 2^29 and > -2^29.
- The conversion from ``unsigned int`' works only if the argument is < 2^29.

In a declaration like ``cl_I x = 10;`' the C++ compiler is able to do the conversion of `10` from ``int`' to ``cl_I`' at compile time already. On the other hand, code like ``cl_I x = 1000000000;`' is in error. So, if you want to be sure that an ``int`' whose magnitude is not guaranteed to be < 2^29 is correctly converted to a ``cl_I`', first convert it to a ``long`'. Similarly, if a large ``unsigned int`' is to be converted to a ``cl_I`', first convert it to an ``unsigned long`'.

Conversions from the C built-in type ``float`' are provided for the classes `cl_FF`, `cl_F`, `cl_R`, `cl_N` and `cl_number`.

Conversions from the C built-in type ``double`' are provided for the classes `cl_DF`, `cl_F`, `cl_R`, `cl_N` and `cl_number`.

Conversions from ``const char *`' are provided for the classes `cl_I`, `cl_RA`, `cl_SF`, `cl_FF`, `cl_DF`, `cl_LF`, `cl_F`, `cl_R`, `cl_N`. The easiest way to specify a value which is outside of the range of the C++ built-in types is therefore to specify it as a string, like this:

       cl_I order_of_rubiks_cube_group = "43252003274489856000";

Note that this conversion is done at runtime, not at compile-time.

Conversions from `cl_I` to the C built-in types ``int`', ``unsigned int`', ``long`', ``unsigned long`' are provided through the functions

`int cl_I_to_int (const cl_I& x)`
`unsigned int cl_I_to_uint (const cl_I& x)`
`long cl_I_to_long (const cl_I& x)`
`unsigned long cl_I_to_ulong (const cl_I& x)`
  Returns `x` as element of the C type ctype. If `x` is not representable in the range of ctype, a runtime error occurs.

Conversions from the classes `cl_I`, `cl_RA`, `cl_SF`, `cl_FF`, `cl_DF`, `cl_LF`, `cl_F` and `cl_R` to the C built-in types ``float`' and ``double`' are provided through the functions

`float float_approx (const type& x)`
`double double_approx (const type& x)`
  Returns an approximation of `x` of C type ctype. If `abs(x)` is too close to 0 (underflow), 0 is returned. If `abs(x)` is too large (overflow), an IEEE infinity is returned.

Conversions from any class to any of its subclasses ("derived classes" in C++ terminology) are not provided. Instead, you can assert and check that a value belongs to a certain subclass, and return it as element of that class, using the ``As`' and ``The`' macros. `As(type)(value)` checks that value belongs to type and returns it as such. `The(type)(value)` assumes that value belongs to type and returns it as such. It is your responsibility to ensure that this assumption is valid. Since macros and namespaces don't go together well, there is an equivalent to ``The`': the template ``the`'.

Example:

       cl_I x = ...;
       if (!(x >= 0)) abort();
       cl_I ten_x_a = The(cl_I)(expt(10,x)); // If x >= 0, 10^x is an integer.
                    // In general, it would be a rational number.
       cl_I ten_x_b = the<cl_I>(expt(10,x)); // The same as above.

[ << ] | [ >> ] |  |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

This document was generated by Richard B. Kreckel on May, 28 2002 using texi2html