[Numpy-discussion] Python 2.1 / Numeric-19.1.0 / kinds

Paul F. Dubois paul at pfdubois.com
Tue Apr 17 20:03:00 EDT 2001

I haven't been able to get into the SourceForge ftp site all day, it has
maxed out its users. I suspect the Python has become too popular...? Anyway,
Pete's zip isn't there yet.

Pete, could you tell me privately the method you use for making the zips? I
have trouble getting the paths right.

I have what will probably be 19.1.0 in CVS now. I have added a package,
kinds, which is a reference implementation of PEP 0424 for your exploration.
It does not install by default on Unix; you have to edit setup_all.py to
make it do that.
I plan to add a floating 32-bit scalar type to this module before
Here is the current text of the PEP:

PEP: 242
Title: Numeric Kinds
Version: $Revision: 1.1 $
Author: paul at pfdubois.com (Paul F. Dubois)
Status: Draft
Type: Standards Track
Created: 17-Mar-2001
Python-Version: 2.2


    This proposal gives the user optional control over the precision
    and range of numeric computations so that a computation can be
    written once and run anywhere with at least the desired precision
    and range.  It is backward compatible with existing code.  The
    meaning of decimal literals is clarified.


    Currently it is impossible in every language except Fortran 90 to
    write a program in a portable way that uses floating point and
    gets roughly the same answer regardless of platform -- or refuses
    to compile if that is not possible.  Python currently has only one
    floating point type, equal to a C double in the C implementation.

    No type exists corresponding to single or quad floats.  It would
    complicate the language to try to introduce such types directly
    and their subsequent use would not be portable.  This proposal is
    similar to the Fortran 90 "kind" solution, adapted to the Python
    environment.  With this facility an entire calculation can be
    switched from one level of precision to another by changing a
    single line.  If the desired precision does not exist on a
    particular machine, the program will fail rather than get the
    wrong answer.  Since coding in this style would involve an early
    call to the routine that will fail, this is the next best thing to
    not compiling.

Supported Kinds of Ints and Floats

    Complex numbers are treated separately below, since Python
    can be built without them.

    Each Python compiler may define as many "kinds" of integer and
    floating point numbers as it likes, except that it must support at
    least two kinds of integer corresponding to the existing int and
    long, and must support at least one kind of floating point number,
    equivalent to the present float.

    The range and precision of the these required kinds are processor
    dependent, as at present, except for the "long integer" kind,
    which can hold an arbitrary integer.

    The built-in functions int(), long(), and float() convert
    inputs to these default kinds as they do at present.  (Note that a
    Unicode string is actually a different "kind" of string and that a
    sufficiently knowledgeable person might be able to expand this PEP
    to cover that case.)

    Within each type (integer, floating) the compiler
    supports a linearly-ordered set of kinds, with the ordering
    determined by the ability to hold numbers of an increased range
    and/or precision.

Kind Objects

    Two new standard functions are defined in a module named
    "kinds".  They return callable objects called kind objects.  Each
    int or floating kind object f has the signature result = f(x), and
    each complex kind object has the signature result = f(x, y=0.).

        For an integer argument n >= 1, return a callable object whose
        result is an integer kind that will hold an integer number in the
        open interval (-10**n,10**n). The kind object
        accepts arguments that are integers including longs.
        If n == 0, returns the kind object corresponding to the Python
        literal 0.

    float_kind(nd, n)
        For nd >= 0 and n >= 1, return a callable object whose result
        is a floating point kind that will hold a floating-point
        number with at least nd digits of precision and a base-10
        exponent in the closed interval [-n, n].  The kind object
        accepts arguments that are integer or float.

        If nd and n are both zero, returns the kind object corresponding
        to the Python literal 0.0.

    The compiler will return a kind object corresponding to the least
    of its available set of kinds for that type that has the desired
    properties.  If no kind with the desired qualities exists in a
    given implementation an OverflowError exception is thrown.  A kind
    function converts its argument to the target kind, but if the
    result does not fit in the target kind's range, an OverflowError
    exception is thrown.

    Besides their callable behavior, kind objects have attributes
    giving the traits of the kind in question.
    1. name is the name of the kind. The standard kinds are called
       int, long, double.
    2. typecode is a single-letter string that would be appropriate
       for use with Numeric or module array to form an array of this
       kind. The standard types' typecodes are 'i', 'O', 'd'
    3. Integer kinds have these additional attributes:
       MAX, equal to the maximum permissible integer of this kind, or
       None for the long kind.
       MIN, equal to the most negative permissible integer of this kind,
       or None for the long kind.
    4. Float kinds have these additional attributes whose properties are
       equal to the corresponding value for the corresponding C type
       in the standard header file "float.h".
       MIN_EXP, MIN_10_EXP,
       RADIX, ROUNDS (== FLT_RADIX, FLT_ROUNDS in float.h)
       These values are of type integer except for MAX, MIN, and EPSILON,
       which are of the Python floating type to which the kind

Attributes of Module kinds

    int_kinds is a list of the available integer kinds, sorted from lowest
              to highest kind.
              By definition, int_kinds[-1] is the long kind.

    float_kinds is a list of the available floating point kinds, sorted
                from lowest to highest kind.

    default_int_kind is the kind object corresponding to the Python
                     literal 0

    default_long_kind is the kind object corresponding to the Python
                      literal 0L

    default_float_kind is the kind object corresponding to the Python
                       literal 0.0

Complex Numbers

    If supported, omplex numbers have real and imaginary parts that are
    floating-point numbers with the same kind.  A Python compiler must
    support a complex analog of each floating point kind it supports,
    if it supports complex numbers at all.

    If complex numbers are supported, the following are available in
    module kinds:

    complex_kind(nd, n)
        Return a callable object whose result is a complex kind that
        will hold a complex number each of whose components
        (.real, .imag) is of kind float_kind(nd, n).  The kind object
        will accept one argument that is of any integer, real, or
        complex kind, or two arguments, each integer or real.

    complex_kinds is a list of the available complex kinds, sorted
                  from lowest to highest kind.

    default_complex_kind = is the kind object corresponding to the
                           Python literal 0.0j.
                           The name of this kind is doublecomplex,
                           and its typecode is 'D'.

    Complex kind objects have these addition attributes:
       floatkind is the kind object of the corresponding float type.


    In module myprecision.py:

        import kinds
        tinyint = kinds.int_kind(1)
        single = kinds.float_kind(6, 90)
        double = kinds.float_kind(15, 300)
        csingle = kinds.complex_kind(6, 90)

    In the rest of my code:

        from myprecision import tinyint, single, double, csingle
        n = tinyint(3)
        x = double(1.e20)
        z = 1.2
        # builtin float gets you the default float kind, properties unknown
        w = x * float(x)
        # but in the following case we know w has kind "double".
        w = x * double(z)

        u = csingle(x + z * 1.0j)
        u2 = csingle(x+z, 1.0)

    Note how that entire code can then be changed to a higher
    precision by changing the arguments in myprecision.py.

    Comment: note that you aren't promised that single != double; but
    you are promised that double(1.e20) will hold a number with 15
    decimal digits of precision and a range up to 10**300 or that the
    float_kind call will fail.

Open Issues

    No open issues have been raised at this time.


    This document has been placed in the public domain.

More information about the NumPy-Discussion mailing list