PEP: 242 Title: Numeric Kinds Author: Paul F. Dubois <paul@pfdubois.com>
Status: Withdrawn Type: Standards Track Created: 17-Mar-2001
Python-Version: 2.2 Post-History: 17-Apr-2001

The kinds module will not be added to the standard library.

There was no opposition to the proposal but only mild interest in using
it, not enough to justify adding the module to the standard library.
Instead, it will be made available as a separate distribution item at
the Numerical Python site. At the next release of Numerical Python, it
will no longer be a part of the Numeric distribution.

Abstract

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.

Rationale

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

int_kind(n)

    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' respectively.
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". MAX, MIN, DIG, MANT_DIG, EPSILON,
    MAX_EXP, MAX_10_EXP, 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 corresponds.

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, complex 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.

Examples

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.

Copyright

This document has been placed in the public domain.