Permalink
Cannot retrieve contributors at this time
246 lines (183 sloc)
8.39 KB
| PEP: 242 | |
| Title: Numeric Kinds | |
| Version: $Revision$ | |
| Last-Modified: $Date$ | |
| Author: paul@pfdubois.com (Paul F. Dubois) | |
| Status: Rejected | |
| Type: Standards Track | |
| Content-Type: text/x-rst | |
| Created: 17-Mar-2001 | |
| Python-Version: 2.2 | |
| Post-History: 17-Apr-2001 | |
| 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. | |
| Rejection | |
| ========= | |
| This PEP has been closed by the author. 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. | |
| Copyright | |
| ========= | |
| This document has been placed in the public domain. | |
| .. | |
| Local Variables: | |
| mode: indented-text | |
| indent-tabs-mode: nil | |
| End: |