Search                        Top                                  Index
REF NUMBERS                                        John Gibson Feb 1995

       COPYRIGHT University of Sussex 1995. All Rights Reserved.

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<                             >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<    PROCEDURES ON NUMBERS    >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<                             >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

This file  describes  all the  Poplog  number types  and  procedures  to
operate on  them.  For  information on  the  textual  representation  of
different kinds of numbers in Pop-11 see REF * ITEMISE.
    All the Poplog  arithmetic operations described  in this file  check
the types of their arguments at run  time in order to determine what  to
do. Fast,  non-checking (hence  more efficient)  integer operations  are
described in REF * FASTPROCS.

         CONTENTS - (Use <ENTER> g to access required sections)

  1   Overview
      1.1   Note on Terminology
      1.2   Fast integer operations
      1.3   Computation on Real Numbers
      1.4   Complex Numbers
      1.5   Conventions for Formal Parameters of Procedures

  2   Predicates Relating to Numbers

  3   Comparisons on Numbers

  4   Number Representation

  5   Arithmetic Operations

  6   Rational and Integer Specific Operations

  7   Complex Specific Operations

  8   Mathematical Functions

  9   Trigonometric Functions

 10   Bitwise/Logical Integer Operations

 11   Random Numbers

 12   Floating_Point Utilities

 13   Numeric Constants
      13.1  Integer
      13.2  Floating-Point

 14   Miscellaneous



-----------
1  Overview
-----------

From Version 12  of Poplog, support  is provided for  all the  numerical
data types  and  associated  functions  specified  by  the  Common  LISP
standard. This  includes  integers,  bigintegers,  decimals,  ddecimals,
ratios and complex numbers.

This REF file describes the Pop-11 interface to these facilities, which,
while differing in some  minor respects from  the Common LISP  standard,
(and of course employing different names for procedures in many  cases),
is essentially an upward-compatible version of it. For a description  of
the Clisp version see Guy L. Steele, Common LISP: The Language,  Chapter
12.

For information  on the  textual representation  of different  kinds  of
numbers in Pop-11 see REF * ITEMISE.

All the Poplog arithmetic  operations described in  this REF file  check
the types of their arguments at run  time in order to determine what  to
do. Fast,  non-checking (hence  more efficient)  integer operations  are
described in REF * FASTPROCS.


1.1  Note on Terminology
------------------------
In this document the term 'integer'  means either a simple integer  or a
biginteger, except  where  explicitly qualified  as  one or  the  other;
'integral' means the same in  the nominal sense, or 'integer-valued'  as
an adjective. Similarily,  'real' means  'non-complex', or  'non-complex
number' - it does NOT carry the meaning of 'floating-point number'  used
by some programming languages.


1.2  Fast integer operations
----------------------------
All Poplog  arithmetic operations  described below  check the  types  of
their arguments at run time in order to determine what to do. This gives
great flexibility  in  writing generic  procedures,  but can  mean  that
efficiency is  sacrificed.  As  a partial  solution  fast,  non-checking
integer operations are provided. These are described in REF * FASTPROCS.
Further   information    concerning   efficiency    is   available    in
HELP * EFFICIENCY.


1.3  Computation on Real Numbers
--------------------------------
Overall,  the  number   types  in  Poplog   provide  three  classes   of
representation:

      # rational  (simple  integers,  bigintegers  and  ratios)
      # simple floating-point  (decimals)
      # double-length floating-point (ddecimals)

These classes are orthogonal in the  sense that a given real number  has
exactly one  representation in  each  format (the  representation  being
exact for rational, and approximate for floating-point).

In particular for rationals, this implies that integers, bigintegers and
ratios are mutually disjoint in terms of the numbers they represent, and
that no two ratios represent the  same number unless they have the  same
numerator and denominator. In other words, a ratio is always reduced  to
its lowest common terms, by dividing numerator and denominator by  their
greatest common divisor; if this makes  the denominator equal to 1,  the
result  is  the  integer  numerator  (this  is  the  rule  of  'rational
canonicalisation'). Any integral result  from a computation will  always
produce a simple  integer (rather  than a biginteger)  if it  can be  so
represented.

With very few exceptions, numerical  procedures are 'generic', that  is,
will accept any kind  of numbers as arguments.  For all procedures  that
perform  a  rational-type  function  (i.e.  excluding  'irrational'   or
'transcendental' functions like  sqrt, log,  etc), rational  argument(s)
will cause the computation to be done using rational arithmetic, and the
result will be rational. Otherwise, when the function is irrational,  or
one or  more  of the  arguments  is floating-point,  all  arguments  are
converted to double-length floating-point and the computation  performed
with double-float  arithmetic  (this  is  the  rule  of  'floating-point
contagion'). The result  will be  a floating-point  whose actual  format
depends on the value of the variable popdprecision (see the  description
of this below).


1.4  Complex Numbers
--------------------
Complex numbers always have  both real and imaginary  parts of the  same
representation class, and so may similarily be sub-divided into rational
complex, simple-float-complex and double-float-complex. Aside from those
irrational functions  that can  produce  a complex  result from  a  real
argument (sqrt applied to a negative real, for example), the only way of
constructing complex numbers is  with the operators  +: and -:  ('plus i
times' and  'minus i  times'). These  obey the  same rules  as for  real
arithmetic: with both arguments rational or rational-complex the  result
is a rational-complex; if either argument is floating or  float-complex,
the result is a float-complex whose format depends on popdprecision.

The  only   rider   to   this   is   the   rule   of   'rational-complex
canonicalisation', which prevents the  production of a  rational-complex
number with a  0 imaginary part.  Instead, the result  is just the  real
part of  the number.  Note that  this does  not apply  to  float-complex
numbers, which can have a 0.0 imaginary part.

Most numerical  procedures  allow  complex or  mixed  real  and  complex
arguments. In a similar  way to the  rational/float distinction for  the
real case, computations  are done  in real arithmetic  producing a  real
result if  all  arguments  are  real, or  otherwise  all  arguments  are
converted to complex and  the operation performed  in complex to  give a
complex result (the rule of 'complex contagion').


1.5  Conventions for Formal Parameters of Procedures
----------------------------------------------------
Except in those cases where the arguments or results of a procedure  are
sufficiently complicated to require names that describe their  function,
rather than just their type, the following conventions are used:

        Convention                Data type
        ----------                ---------
        item                      anything
        bool                      a boolean, true or false
        num, num1, num2           any number, including complex
        real, real1, real2        any number except complex
        N                         a simple integer
        int, int1, int2           any integer, simple or big
        float, float1, float2     a decimal or ddecimal




---------------------------------
2  Predicates Relating to Numbers
---------------------------------

isinteger(item) -> bool                                      [procedure]
        Returns true if item is a simple integer, false otherwise.


isbiginteger(item) -> bool                                   [procedure]
        Returns true if item is a biginteger, false otherwise.


isintegral(item) -> bool                                     [procedure]
        Returns true if item is a simple integer or a biginteger,  false
        otherwise.


isratio(item) -> bool                                        [procedure]
        Returns true if item is a ratio, false otherwise.


isrational(item) -> bool                                     [procedure]
        Returns true  if item  is a  simple integer,  a biginteger  or a
        ratio, and false otherwise.


isdecimal(item) -> bool                                      [procedure]
        Returns  true  if  item  is  a  decimal  or  a  ddecimal,  false
        otherwise.


issdecimal(item) -> bool                                     [procedure]
        Returns true if item is a simple decimal, false otherwise.


isddecimal(item) -> bool                                     [procedure]
        Returns true if item is a ddecimal, false otherwise.


isreal(item) -> bool                                         [procedure]
        Returns true  if item  is  any number  except a  complex,  false
        otherwise.


iscomplex(item) -> bool                                      [procedure]
        Returns true if item is a complex number, false otherwise.


isnumber(item) -> bool                                       [procedure]
        Returns true if item is any kind of number, false otherwise.




-------------------------
3  Comparisons on Numbers
-------------------------

num1  =   num2  ->  bool                                    [operator 7]
num1  /=  num2  ->  bool                                    [operator 7]
        On  numbers,  these  operators   compare  their  arguments   for
        mathematical equality and inequality respectively.

        Different types of rationals (integers, bigintegers and  ratios)
        can never represent the same number  and so can never be  equal;
        comparisons between floating-point (decimals and ddecimals)  and
        between  floating-point   and  rationals   first  convert   both
        arguments to double float.

        The same rules apply to the comparison of the real and imaginary
        parts of a  complex numbers.  Note that a  real number  compared
        with a complex number will be  equal only if the complex  number
        is a float-complex with 0.0 imaginary part (since the  imaginary
        part of a rational  complex must always  be non-zero). See  also
        REF * DATA, HELP * EQUAL.


real1  <   real2  ->  bool                                  [operator 6]
real1  <=  real2  ->  bool                                  [operator 6]
real1  >   real2  ->  bool                                  [operator 6]
real1  >=  real2  ->  bool                                  [operator 6]
        These operators compare their first argument to be  respectively
        less than, less than or equal, greater than, and greater than or
        equal  to  their  second  argument,  where  comparisons  between
        different number  types are  performed  as for  = and  /=.  Both
        arguments must be real numbers.


max(real1, real2)  ->  greatest                              [procedure]
min(real1, real2)  ->  least                                 [procedure]
        max returns the greatest of its two arguments and min the least,
        where 'greatest' means closest to positive infinity, and 'least'
        means closest to negative infinity. Both arguments must be  real
        numbers.


num1  ==#  num2  ->  bool                                   [operator 7]
        Returns true  if  num1 and  num2  are identical  (i.e.  ==),  or
        numbers of  the same  representational type  and numeric  value.
        Decimals and  ddecimals are  NOT considered  to be  of the  same
        type. Two complex numbers  are ==# if their  real parts are  ==#
        and their imaginary parts are ==# .




------------------------
4  Number Representation
------------------------

popdprecision -> bool_or_word                                 [variable]
bool_or_word -> popdprecision
        The value of  this variable controls  the production of  results
        from floating-point computations, in combination with the  types
        of the  arguments supplied  to the  relevant procedure.  In  the
        following, "decimal"  includes  decimal-complex  and  "ddecimal"
        includes  ddecimal-complex   (when  a   complex   floating-point
        operation is involved):

            Value       Effect
            -----       ------
            false       Simple decimal results are always produced.

            the word    A ddecimal  result is  produced only  if one  or
            "ddecimal"  other (or the only) argument was ddecimal. (This
                        is the behaviour specified by Common LISP.)

            any other   Same  as  the   previous  case,  except   that a
                        ddecimal result  is also  produced when  neither
                        argument  is   a   simple  decimal,   i.e.   all
                        argument(s) are ddecimal or rational.

        (Note that  in NO  case is  there an  increase in  precision  of
        floating point computations if all arguments are simple  decimal
        to start with.)

        The default value of popdprecision is false.


pop_reduce_ratios -> bool                                     [variable]
bool -> pop_reduce_ratios
        It was stated above that a ratio result is always reduced to its
        lowest common terms (and therefore to an integral result if  the
        denominator becomes 1). However, in situations where a  rational
        computation  is  being  performed  that  involves  a  number  of
        intermediate results,  the continual  reduction of  intermediate
        values can be time-consuming; this boolean variable is therefore
        provided to enable  reduction to  be turned off  (by setting  it
        false).

        Although  unreduced  ratios   will  give   correct  results   in
        computation, comparisons on them  may not do  so (e.g. 2/2  will
        not come out = to 1),  so this facility must be used  CAREFULLY:
        pop_reduce_ratios should only  be set false  inside a  procedure
        that has it  as a dynamic  local, and you  should always  ensure
        that the variable is returned to true before producing the final
        result of a computation.


number_coerce(num1, to_num) -> num2                          [procedure]
        Produces a number num2 which is the number num1 converted to the
        representation  class   (i.e.   rational,  simple   decimal   or
        double-float ddecimal) of the number to_num.

        Firstly, no new number is constructed unless necessary; thus  if
        the class  of  num1 already  matches  that of  to_num,  num1  is
        returned unchanged.  Otherwise,  conversion from  one  class  to
        another proceeds.

        Conversion from rational  form to floating-point  form, or  from
        one float format to the other,  takes place in the obvious  way.
        For conversion  from floating-point  to rational,  the float  is
        assumed to be  completely accurate, and  a mathematically  equal
        rational number is returned.

        If num1  is complex,  then num2  is the  complex number  got  by
        applying number_coerce  recursively to  the real  and  imaginary
        parts of num1, i.e.

            number_coerce(realpart(num1), to_num)
                +: number_coerce(imagpart(num1), to_num) -> num2

        If the second argument  to_num is itself  complex, then num1  is
        coerced to a  complex number of  the same representation  class,
        i.e. the result is computed as

            realpart(to_num) -> to_num;
            number_coerce(realpart(num1), to_num)
                +: number_coerce(imagpart(num1), to_num) -> num2

        where imagpart will fill  in an appropriate  zero value for  the
        imaginary part if num1 is real.




------------------------
5  Arithmetic Operations
------------------------

num1  +  num2  ->  num3                                     [operator 5]
num1  -  num2  ->  num3                                     [operator 5]
num1  *  num2  ->  num3                                     [operator 4]
num1  /  num2  ->  num3                                     [operator 4]
        These operators respectively add, subtract, multiply and  divide
        their arguments,  which may  be  any numbers.  The type  of  the
        result depends  on  the  rules  of  floating-point  and  complex
        contagion as described above. In particular, note that  dividing
        one integer by another produces a  ratio when the result is  not
        exact.


-  num1  ->  num2                                           [operator 1]
        As a prefix operator, - is equivalent to negate(num1).


divid  //   divis  ->  (rem, quot)                          [operator 4]
divid  div  divis  ->  quot                                 [operator 2]
divid  rem  divis  ->  rem                                  [operator 2]
        The two results returned by the operator // are defined by

                intof(divid / divis) -> quot

        and

                divid - (quot * divis) -> rem

        where the arguments may be  any numbers, including complex.  The
        two results may be obtained separately with div (quot only)  and
        rem (rem only).


divid  mod  divis  ->  mod                                  [operator 2]
        Returns divid modulo  divis, where  both numbers  must be  real.
        This is defined as

            divid rem divis -> rem;
            if (divis > 0 and rem < 0) or (divis < 0 and rem >= 0) then
                rem+divis -> mod
            else
                rem -> mod
            endif

        (i.e. the result always has the same sign as the divisor).


intof(num) -> int                                            [procedure]
        For num real, intof truncates  its argument to an integer,  i.e.
        it returns the  integer of  the same sign  as num  and with  the
        largest magnitude such that abs(int) <= abs(num). For a  complex
        number, the result  is the integral  complex number obtained  by
        applying intof to its parts, i.e.

                intof(realpart(num)) +: intof(imagpart(num))


fracof(num) -> frac                                          [procedure]
        The fractional part of num, defined as

                num - intof(num)


round(num) -> int                                            [procedure]
        For num real, rounds num to an integer by taking  intof(num+1/2)
        if num  is  positive, or  intof(num-1/2)  otherwise. If  num  is
        complex, the result is

                round(realpart(num)) +: round(imagpart(num))


abs(num) -> real                                             [procedure]
        Returns the absolute  value of num,  which (except for  complex)
        will always be a number of  the same type. For any complex  num,
        the result will be a floating-point real, computed as

                sqrt( realpart(num)**2 + imagpart(num)**2 )


negate(num1) -> num2                                         [procedure]
        Returns the negation of num1, which  will always be a number  of
        the same type.


sign(num) -> sign                                            [procedure]
        For num real, sign returns -1, 0  or 1 of the same type as  num,
        depending on whether num is  negative, zero or positive. If  num
        is complex, the result is  a floating-point complex number  such
        that

                abs(sign) = 1.0, phase(sign) = phase(num)




-------------------------------------------
6  Rational and Integer Specific Operations
-------------------------------------------

checkinteger(item, low_int, hi_int)                          [procedure]
        Checks item to be a (simple) integer within the range  specified
        by lower  bound  low_int  and upper  bound  hi_int  (inclusive).
        Either or both bounds may be false to indicate no upper or lower
        limit. If  all conditions  are satisfied  the procedure  returns
        with no action, otherwise a mishap occurs.

        (Note that * fi_check is  a faster version  that does not  check
        its second and  third arguments are  integers, and also  returns
        item if it is an integer within the given range.)


gcd_n(int1, int2, ..., intN, N) -> gcd                       [procedure]
        Computes the greatest common divisor  of the all the N  integers
        int1, int2,  ..., intN,  where the  number N  itself (a  simple
        integer >= 0) appears as the rightmost argument. If N = 0,  then
        gcd = 0; if N = 1, then gcd = int1.


lcm_n(int1, int2, ..., intN, N) -> lcm                       [procedure]
        Computes the least  common multiple  of the all  the N  integers
        int1, int2,  ..., intN,  where  the number  N itself  (a  simple
        integer >= 0) appears as the rightmost argument. If N = 0,  then
        lcm = 1; if N = 1, then lcm = int1.


destratio(rat)   -> (numerator, denominator)                 [procedure]
numerator(rat)   -> numerator                                [procedure]
denominator(rat) -> denominator                              [procedure]
        These procedures return the numerator and denominator parts of a
        rational number,  either  together  (destratio),  or  separately
        (numerator  and  denominator).  When   rat  is  integral,   then
        numerator = rat, and denominator = 1.




------------------------------
7  Complex Specific Operations
------------------------------

num1  +:  num2  ->  num3                                    [operator 5]
num1  -:  num2  ->  num3                                    [operator 5]
        These two  operators  are  the basic  way  of  creating  complex
        numbers. Effectively, they both  multiply their second  argument
        by "i" (the positive square root of -1), and then either add the
        result to  (+:)  or subtract  the  result from  (-:)  the  first
        argument.


+: num1  ->  num2                                           [operator 1]
-: num1  ->  num2                                           [operator 1]
        As prefix operators, +: and -: are equivalent to  unary_+:(num1)
        and unary_-:(num1) respectively.


unary_+:(num1)  ->  num2                                     [procedure]
unary_-:(num1)  ->  num2                                     [procedure]
        Single-argument versions  of +:  and  -:, which  multiply  their
        argument by i and -i respectively.


conjugate(num1) -> num2                                      [procedure]
        Returns the complex conjugate of its argument. The conjugate  of
        a real number is itself, while for a complex number it is

                realpart(num1) -: imagpart(num1)

        i.e. a  complex  number  with the  same  realpart,  but  negated
        imagpart.


destcomplex(num) -> (realpart, imagpart)                     [procedure]
realpart(num)    -> realpart                                 [procedure]
imagpart(num)    -> imagpart                                 [procedure]
        These procedures  return  the  real  and  imaginary  parts  of a
        complex number,  either  together (destcomplex),  or  separately
        (realpart and imagpart). When num is real, then realpart =  num,
        and a zero of the same type as num is returned for imagpart.




-------------------------
8  Mathematical Functions
-------------------------

The procedures  in  this  section, as  well  as  most of  those  in  the
following section  on  trigonometric  procedures,  compute  mathematical
functions whose definitions on the complex plane necessitate choices  of
branch cuts and principal values. See the section Branch Cuts, Principal
Values and Boundary Conditions  in Chapter 12 of  Steele for details  of
these.


sqrt(num) -> sqrt                                            [procedure]
        Returns the (principal) square root of num. This will be a  real
        floating-point  if   num  is   real  and   non-negative,   and a
        float-complex otherwise.


log(num) -> log                                              [procedure]
        Returns the natural (base e) logarithm of num, which must not be
        a zero of any kind. If num is real and non-negative, the  result
        is a  real floating-point.  Otherwise, it  is the  float-complex
        number

                log(abs(num)) +: phase(num)


log10(num) -> log                                            [procedure]
        Returns the base 10 logarithm of  num, which must not be a  zero
        of any kind. Defined as

               log(num) / log(10)


exp(num) -> exponent                                         [procedure]
        Returns e  raised to  the power  num,  where e  is the  base  of
        natural logarithms.  The  result  is  a  floating-point  if  the
        argument is real, or a float-complex otherwise.


base **  power -> exponent                                  [operator 3]
        Returns base raised to the power power, where either may be  any
        numbers (except that base must not  be zero if power is zero  of
        any type other than integer 0).

        If  power  is  an  integer,  the  computation  is  performed  by
        successively  multiplying  powers  of  base;  thus  if  base  is
        rational, the result will be exact.  If power is integer 0,  the
        result is always a 1 of the same type as base.

        Otherwise, if power is not an integer, the result is computed as

               exp(power * log(base))




--------------------------
9  Trigonometric Functions
--------------------------

All the procedures in this section take an angle as argument, or  return
one as a  result. In  both cases,  the units  of the  angle (radians  or
degrees) are controlled by the boolean variable popradians (this applies
equally when the angle is complex).

The following diagram of the real  plane is provided as a  visualisation
aid (angles are given in radians):


             Quadrant II       |        Quadrant I
            x < 0, y >= 0      |      x >= 0, y >= 0
         pi/2 < angle <= pi    |    0 <= angle <= pi/2
                               |
                               |
                        pi-A   |  A
                             \ | /
                ---------------+--------------
                             / | \
                      -(pi-A)  |  -A
                               |
                               |
             Quadrant III      |       Quadrant IV
             x < 0, y < 0      |      x >= 0, y < 0
         -pi < angle < -pi/2   |    -pi/2 <= angle < 0


(The above is particularly useful in relation to arctan2.)


popradians -> bool                                            [variable]
bool -> popradians
        This boolean variable specifies whether the angle arguments  for
        trigonometric procedures such  as sin,  cos etc  are in  radians
        (true) or degrees (false). Similarily, it controls the units  of
        angle results produced by procedures like arcsin, arccos, etc.

        Note that  the  default  value  is  false,  implying  angles  in
        degrees.


phase(num) -> realangle                                      [procedure]
        Returns the  complex  phase angle  of  num as  a  floating-point
        quantity. This will be in the range

                - pi < realangle <= pi    (radians)
               - 180 < realangle <= 180   (degrees)

        If num is real, then realangle = 0.0. This procedure is  defined
        as

                arctan2(destcomplex(num))


cis(realangle) -> num                                        [procedure]
        Returns the float-complex number

                cos(realangle) +: sin(realangle)

        (the name cis standing for 'cos + i sin'). Note that this is the
        same as

                exp(+: realangle)

        ONLY  when  popradians  is  true  (because  the  latter   always
        interprets realangle in radians).


sin(angle) -> num                                            [procedure]
cos(angle) -> num                                            [procedure]
tan(angle) -> num                                            [procedure]
        These procedures compute the sine, cosine and tangent of  angle.
        The result is a floating-point,  or a float-complex if angle  is
        complex.


arcsin(num) -> angle                                         [procedure]
arccos(num) -> angle                                         [procedure]
arctan(num) -> angle                                         [procedure]
        These procedures compute the  arcsine, arccosine and  arctangent
        of num. For num complex, the result is a float-complex. For  num
        real, it  is a  real float,  except in  the case  of arcsin  and
        arccos when abs(num) > 1.

        For arctan, it is an error if num = +:1 or -:1.


arctan2(real_x, real_y) -> realangle                         [procedure]
        Computes the arctangent of real_y / real_x, but using the  signs
        of the two numbers to derive quadrant information. The result is
        a floating-point number in the range

                - pi < realangle <= pi    (radians)
               - 180 < realangle <= 180   (degrees)

        (see diagram above). When real_x =  0 and real_y = 0 the  result
        is defined (arbitrarily) to be 0.0.


sinh(angle) -> num                                           [procedure]
cosh(angle) -> num                                           [procedure]
tanh(angle) -> num                                           [procedure]
        These procedures compute the hyperbolic sine, hyperbolic  cosine
        and hyperbolic tangent of angle. The result is a floating-point,
        or a float-complex if angle is complex.


arcsinh(num) -> angle                                        [procedure]
arccosh(num) -> angle                                        [procedure]
arctanh(num) -> angle                                        [procedure]
        These procedures  compute  the  hyperbolic  arcsine,  hyperbolic
        arccosine and hyperbolic arctangent of num. For num complex, the
        result is a float-complex.  For num real,  the result will  be a
        real float, except in the following cases:

                arccosh:  num < 1
                arctanh:  abs(num) > 1

        For arctanh, it is an error if num = 1 or -1.


pi -> float                                                   [constant]
        This constant  is the  best  ddecimal approximation  to  "pi", =
        3.14159....




--------------------------------------
10  Bitwise/Logical Integer Operations
--------------------------------------

These procedures  enable  integers to  be  manipulated as  bit  patterns
representing two's-complement values,  where bit position  N has  weight
2**N (i.e. bits are numbered  from 0 upwards). Note that  (conceptually,
at any rate), the sign bit of an integer is extended indefinitely to the
left. Thus everywhere above its most significant bit, a positive integer
has 0 bits and a negative integer has 1 bits.


int1 && int2 -> int3                                        [operator 4]
        The result  of  this  operation  is the  logical  "and"  of  the
        integers int1 and int2, i.e. there is a 1 in the result for each
        bit position for which there is a 1 in both int1 and int2.


int1 &&~~ int2 -> int3                                      [operator 4]
        The result of this  operation is the logical  "and" of int1  and
        the logical complement of int2, i.e. there is a 1 in the  result
        for each bit position for which there is a 1 in int1 and a 0  in
        int2. (Same as int1 && (~~int2)  -- useful for clearing bits  of
        int1 set in int2).


int1 || int2 -> int3                                        [operator 4]
        The result of this  operation is the  logical "inclusive or"  of
        int1 and int2,  i.e. there is  a 1  in the result  for each  bit
        position for which there is a 1 in either int1 or int2.


int1 ||/& int2 -> int3                                      [operator 4]
        The result of this  operation is the  logical "exclusive or"  of
        int1 and int2,  i.e. there is  a 1  in the result  for each  bit
        position for which there is a 1  in either int1 or int2 but  not
        in both.


~~ int1 -> int2                                             [operator 4]
        Produces the logical complement of the integer int1, i.e.  there
        is a 1 in the result for each bit position for which int1 has 0.
        It is always true that

                ~~ int = -(int + 1)


int1  <<  N -> int2                                         [operator 4]
        Produces the  bit  pattern  of  int1  shifted  left  by  (simple
        integer) N positions; a  negative value for  N produces a  right
        shift.


int1  >>  N -> int2                                         [operator 4]
        Gives the bit pattern of int1 shifted right by (simple  integer)
        N positions; a negative value for N implies a left shift.


int1  &&/=_0  int2  ->  bool                                [operator 6]
int1  &&=_0   int2  ->  bool                                [operator 6]
        These two operators are equivalent to the boolean expressions

                int1 && int2 /== 0
                int1 && int2  == 0

        but  are  more  efficient  (and  avoid  producing   intermediate
        results).


testbit(int, N) -> bool                                      [procedure]
bool -> testbit(int, N) -> newint
        This procedure and its updater enable the testing and setting or
        clearing of the bit at position  N in the integer int. The  base
        procedure returns the state  of bit N as  a boolean value,  true
        for 1 and false  for 0.

        The updater (which is somewhat unusual for an updater in that it
        returns a result)  produces a  new integer newint  which is  int
        with bit N set to  1 or cleared to 0  as specified by the  input
        bool argument (which may  in fact be  any item, false  meaning 0
        and anything else meaning 1).


integer_leastbit(int) -> N_or_false                          [procedure]
        Returns the bit position N  of the least-significant bit set  in
        the integer int (equivalently,  N is the highest  power of 2  by
        which int divides exactly). false is returned for int 0.


integer_length(int) -> N                                     [procedure]
        Returns the length in bits of int as a two's-complement integer.
        That is, N is the smallest integer such that

                int  <   ( 1 << N)     if int >= 0
                int  >=  (-1 << N)     if int < 0

        Put another way: if int is non-negative then the  representation
        of int as  an unsigned integer  requires a field  of at  least N
        bits; alternatively,  a  minimum of  N+1  bits are  required  to
        represent int as a signed integer, regardless of its sign.


integer_bitcount(int) -> N                                   [procedure]
        Counts the  number  of  1  or 0  bits  in  the  two's-complement
        representation of int. If int  is non-negative, N is the  number
        of 1 bits; if int is negative, it is the number of 0 bits. (Note
        that, owing to the sign extension, there are an infinite  number
        of 0 bits  in a  non-negative integer or  1 bits  in a  negative
        integer.) It is always the case that

               integer_bitcount(int) = integer_bitcount(-(int+1))


integer_field(size, position) -> access_p                    [procedure]
        This procedure is used  to create accessing/updating  procedures
        for  sub-bitfields  within   integers,  and   provides  a   more
        convenient (and more efficient) way of manipulating such  fields
        than by masking and shifting with the operators && and >>, etc.

        Given a specification  of a bitfield  in terms of  its width  in
        bits  size,  and  lowest  bit  position  position  (both  simple
        integers), it returns a procedure access_p. When this is applied
        to any integer it extracts the binary value represented by  bits
        position to  position+size-1  within the  integer  (shifting  it
        right by position bits to align it at bit 0). That is,

                access_p(int) -> field_value

        If the  size argument  is >  0, the  field is  taken to  contain
        unsigned (positive or zero values) only;  if size < 0, then  the
        field is signed,  and upon  extraction, the highest  bit of  the
        field is extended as the sign bit of the resulting value (cf the
        corresponding convention used by conskey).

        The updater of access_p, on the other hand, takes a field  value
        and an integer, and returns a new integer in which the  contents
        of the field bits are replaced by the given value (but which has
        the same bits everywhere else). That is:

                field_value -> access_p(int) -> newint

        Note  that  the  updater  makes   no  check  on  the  range   of
        field_value; bits 0  to size-1  of field_value  are masked  out,
        shifted up,  and inserted  into the  field position  (and it  is
        therefore irrelevant in this context whether the field is signed
        or unsigned).

        A further feature is that access_p  and its updater can be  made
        to merely mask out or mask  in the field bits, without  shifting
        the value down or up. That  is, upon extraction the field  value
        is left aligned at bit position  in the result; the input  value
        for updating  is  assumed  to be  similarily  aligned.  This  is
        achieved by supplying a second argument of false to access_p  or
        its updater, i.e.

             access_p(int, false)  -> unshifted_field_value
             unshifted_field_value -> access_p(int, false) -> newint

        Note also that in this case,  extraction of a signed field  will
        NOT cause it to be sign-extended.




------------------
11  Random Numbers
------------------

The value of the variable ranseed is used as the seed for the generation
of random  numbers,  each random  number  generated using  one  or  more
successive seed  values  (depending  on the  type  of  the  INT_OR_FLOAT
argument to  random0  and random).  The algorithm used to generate  each
successive seed value is

        ranseed fi_* 524269 fi_+ 32749 -> ranseed

which (since  all  current implementations  use  30 bits  for  a  simple
integer) produces a result modulo  2**30. This algorithm has a  verified
cycle length of 2**30, i.e.  it will produce every possible  combination
of 30 bits before repeating a number.


random0(int_or_float) -> random                              [procedure]
        Given a  strictly-positive integer  or floating-point  argument,
        this procedure generates a  random number of  the same type,  in
        the range

                0 <= random < int_or_float

        where the distribution of random will be approximately  uniform.
        The value of the  variable ranseed is used  as the seed for  the
        generation process,  and  is  replaced with  a  new  seed  value
        afterwards.


random(int_or_float) -> random                               [procedure]
        Same as random0, except that whenever the latter would  return 0
        or 0.0, the original argument int_or_float is returned  instead.
        It can thus be defined as

                random0(int_or_float) -> random;
                if random = 0 then int_or_float else random endif;

        Hence the range of the result is 0 < random <= int_or_float  for
        a float, or 1 <= random <= int_or_float for an integer.


ranseed -> n_or_false                                         [variable]
n_or_false -> ranseed
        This variable is used  to hold the next  seed for generation  of
        random numbers by random0 or  random, both of which  side-effect
        it. If  set to  false, it  will be  re-initialised to  a  random
        simple integer  the next  time either  procedure is  called  (by
        using sys_real_time).  Otherwise,  its value  must  always  be a
        simple integer.




----------------------------
12  Floating_Point Utilities
----------------------------

The  procedures  in  this  section  provide  the  means  to   manipulate
floating-point   numbers,   and   make    possible   the   writing    of
machine-independent floating-point  software. Note  that none  of  these
procedures are affected by the value of popdprecision.


float_decode(float, want_int) -> (mantissa, expo, sign)      [procedure]
        This procedure takes a floating-point number and splits it  into
        its component parts, i.e. mantissa, exponent and sign.

        The exponent expo  is always  an integer;  the boolean  argument
        want_int controls whether  the mantissa mantissa  and sign  sign
        are returned as floats or integers.

        sign represents the sign, and is  returned either as a float  or
        an integer. For want_int  false, it is 1.0  or -1.0 of the  same
        type as the argument, and with the same sign (note that sign  is
        1.0 for 0.0). For want_int true, it is 1 or -1 (1 for 0.0).

        Denoting the  radix of  the floating-point  representation  by b
        (see pop_float_radix below), expo is  the integer power of b  by
        which mantissa must be multiplied to regain the magnitude of the
        original number.

        mantissa itself is the absolute value of the number with

                b ** expo

        divided out, and can be returned in one of two ways: If want_int
        is false, then mantissa is returned as a float of the same type,
        in the range

                1/b <= mantissa < 1

        Otherwise, mantissa is is returned as an integer, scaled by

                b ** float_precision(float)

        (that  is,  so   that  the  least   significant  digit  in   the
        representation of float is scaled to unity in the result).  If P
        = float_precision(float) we then have

                b ** (P-1) <= mantissa < b ** P

        Thus whether the mantissa is returned as a float or an  integer,
        it will always be the case that

                mantissa * (b ** expo) = abs(float)

        (Note that this holds also when float = 0.0, since in this  case
        expo = 0, and mantissa is 0.0 or 0 depending on want_int.)


float_scale(float1, expo) -> float2                          [procedure]
        This provides a more efficient way of scaling a float by a power
        of the floating-point radix 'b' than by using ** (and avoids any
        intermediate overflow  or underflow  that could  occur with  the
        latter). It returns

                float1 * (b ** expo)

        as a  floating-point  of the  same  type. If  the  final  result
        overflows or underflows (i.e. the absolute value of the exponent
        is too large for the representation), then false is returned.

        For example,  this procedure  can be  used in  conjunction  with
        float_sign  to  put  back   together  a  floating-point   number
        decomposed with float_decode. That is, after

           float_decode(float, false) -> (mantissa, expo, sign)

        one can use

           float_sign(sign, float_scale(mantissa, expo))

        to retrieve the original number.


float_sign(sign_float, float1) -> float2                     [procedure]
        Returns a  floating-point number  float2 of  the same  type  and
        absolute value as float1,  but which has the  sign of the  float
        sign_float.

        The argument float1 may also be  false. In this case, float2  is
        returned as  a  1.0  or  -1.0  of the  same  type  and  sign  as
        sign_float.


float_digits(float) -> digits                                [procedure]
        Returns,  as  an  integer,   the  number  of  radix-'b'   digits
        represented in the floating-point format of the argument.  (I.e.
        digits has only two  possible values, one  for decimals and  one
        for ddecimals. In all current Poplog implementations, b = 2  and
        ddecimal digits is 53 or 56;  decimal digits is 50 on Alpha  OSF
        and 22 on other platforms.)


float_precision(float) -> sigdigits                          [procedure]
        Same as  float_digits, except  that  the number  of  significant
        radix-'b' digits  in  the  argument is  returned.  Since  Poplog
        floating-point decimals  and  ddecimals are  always  normalised,
        this will in fact be identical to float_digits(float), with  the
        single exception that float_precision(0.0) = 0 for either  float
        type.




---------------------
13  Numeric Constants
---------------------

This section  describes  constants that  define  the ranges  of  numbers
available in various representation classes.


13.1  Integer
-------------

int_parameters                                                 [library]
        This is a library; to use  any of the constants it defines,  you
        must load it explicitly with

                uses int_parameters;

        It defines  the  following  parameter constants,  all  of  whose
        values relate to the particular implementation of Poplog in use:


pop_max_int                                                   [constant]
        The largest (i.e.  most positive) integer  value represented  in
        immediate format (as an integer).


pop_min_int                                                   [constant]
        The smallest (i.e. most  negative) integer value represented  in
        immediate format (as an integer).


13.2  Floating-Point
--------------------

float_parameters                                               [library]
        This is a library; to use  any of the constants it defines,  you
        must load it explicitly with

                uses float_parameters;

        It defines  the  following  parameter constants,  all  of  whose
        values relate to the particular implementation of Poplog in use:


pop_most_positive_decimal                                     [constant]
        The greatest  positive  value representable  in  simple  decimal
        format.


pop_least_positive_decimal                                    [constant]
        The  smallest   positive  (non-zero)   value  representable   in
        simple decimal format.


pop_least_negative_decimal                                    [constant]
        The least negative value (i.e. closest to zero) representable in
        simple decimal format.


pop_most_negative_decimal                                     [constant]
        The most  negative value  (i.e.  closest to  negative  infinity)
        representable in simple decimal format.


pop_most_positive_ddecimal                                    [constant]
pop_least_positive_ddecimal                                   [constant]
pop_least_negative_ddecimal                                   [constant]
pop_most_negative_ddecimal                                    [constant]
        Same as the above for double-length ddecimal format.


pop_plus_epsilon_decimal                                      [constant]
pop_plus_epsilon_ddecimal                                     [constant]
        These are the  smallest positive  numbers in  each format  which
        when added to 1.0 of the  same format produce a value not  equal
        to 1.0. I.e. for each format, the smallest positive e such that

               number_coerce(1, e) + e /= 1.0

        is  true.   (N.B.  For   ddecimal   format,  this   depends   on
        popdprecision not being false.)


pop_minus_epsilon_decimal                                     [constant]
pop_minus_epsilon_ddecimal                                    [constant]
        Same as before, but subtracting from 1.0 instead of adding, i.e.
        for each format the smallest positive e such that

                number_coerce(1, e) - e /= 1.0

        is true.


pop_float_parameters -> fvec                                  [constant]
        This  is  a  full  vector  that  holds  all  the  floating-point
        constants given above, and which the latter uses to define  each
        value as a separate  constant. You are not  advised to use  this
        directly (since its format may change); always access the values
        via LIB * FLOAT_PARAMETERS.


pop_float_radix -> int                                        [constant]
        This  integer  constant  is  the  radix  of  the  floating-point
        representation (= 2 in all current Poplog implementations).




-----------------
14  Miscellaneous
-----------------

linearfit(list) -> (m, c)                                    [procedure]
        Takes a list  of pairs of  numbers representing co-ordinates  of
        points, works out the best straight line through the points, and
        returns its  slope m,  and its  Y-intercept c.  For vertical  or
        nearly lines it will produce an error. See LIB * LINEARFIT.


integer_key    -> key                                         [constant]
biginteger_key -> key                                         [constant]
ratio_key      -> key                                         [constant]
decimal_key    -> key                                         [constant]
ddecimal_key   -> key                                         [constant]
complex_key    -> key                                         [constant]
        Structure  keys  for   simple  integers,  bigintegers,   ratios,
        decimals, ddecimals and complex numbers (see REF * KEYS).



--- C.all/ref/numbers
--- Copyright University of Sussex 1995. All rights reserved.