205 lines
7.8 KiB
Plaintext
205 lines
7.8 KiB
Plaintext
|
PEP: 242
|
|||
|
Title: Numeric Kinds
|
|||
|
Version: $Revision$
|
|||
|
Author: paul@pfdubois.com (Paul F. Dubois)
|
|||
|
Status: Draft
|
|||
|
Type: Standards Track
|
|||
|
Created: 17-Mar-2001
|
|||
|
Python-Version: 2.2
|
|||
|
Post-History:
|
|||
|
|
|||
|
|
|||
|
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
|
|||
|
|
|||
|
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 kinds are processor dependent, as at present, except for the
|
|||
|
"long integer" kind, which can hold an arbitrary integer. The
|
|||
|
built-in functions int(), float(), long() and complex() 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, and complex) 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
|
|||
|
|
|||
|
Three 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 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). This function always succeeds, since
|
|||
|
it can return the 'long' kind if it has to. The kind object
|
|||
|
accepts arguments that are integers including longs. If n ==
|
|||
|
0, returns the kind object corresponding to long.
|
|||
|
|
|||
|
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 open interval (-n, n). The kind object
|
|||
|
accepts arguments that are integer or real.
|
|||
|
|
|||
|
complex_kind(nd, n)
|
|||
|
Return a callable object whose result is a complex kind that
|
|||
|
will 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 integer, real, or complex, or
|
|||
|
two arguments, each integer or real.
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
Kind objects also accept a string argument for conversion of
|
|||
|
literal notation to their kind.
|
|||
|
|
|||
|
Besides their callable behavior, kind objects have attributes
|
|||
|
giving the traits of the kind in question. The list of traits
|
|||
|
needs to be completed.
|
|||
|
|
|||
|
|
|||
|
The Meaning of Literal Values
|
|||
|
|
|||
|
Literal integer values without a trailing L are of the least
|
|||
|
integer kind required to represent them. An integer literal with
|
|||
|
a trailing L is a long. Literal decimal values are of the
|
|||
|
greatest available binary floating-point kind.
|
|||
|
|
|||
|
|
|||
|
Concerning Infinite Floating Precision
|
|||
|
|
|||
|
This section makes no proposals and can be omitted from
|
|||
|
consideration. It is for illuminating an intentionally
|
|||
|
unimplemented 'corner' of the design.
|
|||
|
|
|||
|
This PEP does not propose the creation of an infinite precision
|
|||
|
floating point type, just leaves room for it. Just as int_kind(0)
|
|||
|
returns the long kind object, if in the future an infinitely
|
|||
|
precise decimal kind is available, float_kind(0,0) could return a
|
|||
|
function that converts to that type. Since such a kind function
|
|||
|
accepts string arguments, programs could then be written that are
|
|||
|
completely precise. Perhaps in analogy to r'a raw string', 1.3r
|
|||
|
might be available as syntactic sugar for calling the infinite
|
|||
|
floating kind object with argument '1.3'. r could be thought of
|
|||
|
as meaning 'rational'.
|
|||
|
|
|||
|
|
|||
|
Complex numbers and kinds
|
|||
|
|
|||
|
Complex numbers are always pairs of 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.
|
|||
|
|
|||
|
|
|||
|
Coercion
|
|||
|
|
|||
|
In an expression, coercion between different kinds is to the
|
|||
|
greater kind. For this purpose, all complex kinds are "greater
|
|||
|
than" all floating-point kinds, and all floating-point kinds are
|
|||
|
"greater than" all integer kinds.
|
|||
|
|
|||
|
|
|||
|
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)
|
|||
|
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
|
|||
|
|
|||
|
The assertion that a decimal literal means a binary floating-point
|
|||
|
value of the largest available kind is in conflict with other
|
|||
|
proposals about Python's numeric model. This PEP asserts that
|
|||
|
these other proposals are wrong and that part of them should not
|
|||
|
be implemented.
|
|||
|
|
|||
|
Determine the exact list of traits for integer and floating point
|
|||
|
numbers. There are some standard Fortran routines that do this
|
|||
|
but I have to track them down. Also there should be information
|
|||
|
sufficient to create a Numeric array of an equal or greater kind.
|
|||
|
|
|||
|
|
|||
|
Copyright
|
|||
|
|
|||
|
This document has been placed in the public domain.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Local Variables:
|
|||
|
mode: indented-text
|
|||
|
indent-tabs-mode: nil
|
|||
|
End:
|