This version includes the input from various and sundry people. Thanks to everyone who contributed.
<mike PEP: XXX Title: A rational number module for Python Version: $Revision: 1.4 $ Last-Modified: $Date: 2003/09/22 04:51:50 $ Author: Mike Meyer <[EMAIL PROTECTED]> Status: Draft Type: Staqndards Content-Type: text/x-rst Created: 16-Dec-2004 Python-Version: 2.5 Post-History: 15-Dec-2004, 25-Dec-2004 Contents ======== * Abstract * Motivation * Rationale + Conversions + Python usability * Specification + Explicit Construction + Implicit Construction + Operations + Exceptions * Open Issues * Implementation * References Abstract ======== This PEP proposes a rational number module to add to the Python standard library. Motivation ========= Rationals are a standard mathematical concept, included in a variety of programming languages already. Python, which comes with 'batteries included' should not be deficient in this area. When the subject was brought up on comp.lang.python several people mentioned having implemented a rational number module, one person more than once. In fact, there is a rational number module distributed with Python as an example module. Such repetition shows the need for such a class in the standard library. n There are currently two PEPs dealing with rational numbers - 'Adding a Rational Type to Python' [#PEP-239] and 'Adding a Rational Literal to Python' [#PEP-240], both by Craig and Zadka. This PEP competes with those PEPs, but does not change the Python language as those two PEPs do [#PEP-239-implicit]. As such, it should be easier for it to gain acceptance. At some future time, PEP's 239 and 240 may replace the ``rational`` module. Rationale ========= Conversions ----------- The purpose of a rational type is to provide an exact representation of rational numbers, without the imprecistion of floating point numbers or the limited precision of decimal numbers. Converting an int or a long to a rational can be done without loss of precision, and will be done as such. Converting a decimal to a rational can also be done without loss of precision, and will be done as such. A floating point number generally represents a number that is an approximation to the value as a literal string. For example, the literal 1.1 actually represents the value 1.1000000000000001 on an x86 one platform. To avoid this imprecision, floating point numbers cannot be translated to rationals directly. Instead, a string representation of the float must be used: ''Rational("%.2f" % flt)'' so that the user can specify the precision they want for the floating point number. This lack of precision is also why floating point numbers will not combine with rationals using numeric operations. Decimal numbers do not have the representation problems that floating point numbers have. However, they are rounded to the current context when used in operations, and thus represent an approximation. Therefore, a decimal can be used to explicitly construct a rational, but will not be allowed to implicitly construct a rational by use in a mixed arithmetic expression. Python Usability ----------------- * Rational should support the basic arithmetic (+, -, *, /, //, **, %, divmod) and comparison (==, !=, <, >, <=, >=, cmp) operators in the following cases (check Implicit Construction to see what types could OtherType be, and what happens in each case): + Rational op Rational + Rational op otherType + otherType op Rational + Rational op= Rational + Rational op= otherType * Rational should support unary operators (-, +, abs). * repr() should round trip, meaning that: m = Rational(...) m == eval(repr(m)) * Rational should be immutable. * Rational should support the built-in methods: + min, max + float, int, long + str, repr + hash + bool (0 is false, otherwise true) When it comes to hashes, it is true that Rational(25) == 25 is True, so hash(Rational (25)) should be equal to hash(25). The detail is that you can NOT compare Rational to floats, strings or decimals, so we do not worry about them giving the same hashes. In short: hash(n) == hash(Rational(n)) # Only if n is int, long or Rational Regarding str() and repr() behaviour, Ka-Ping Yee proposes that repr() have the same behaviour as str() and Tim Peters proposes that str() behave like the to-scientific-string operation from the Spec. Specification ============= Explicit Construction --------------------- The module shall be ``rational``, and the class ``Rational``, to follow the example of the decimal [#PEP-327] module. The class creation method shall accept as arguments a numerator, and an optional denominator, which defaults to one. Both the numerator and denominator - if present - must be of integer or decimal type, or a string representation of a floating point number. The string representation of a floating point number will be converted to rational without being converted to float to preserve the accuracy of the number. Since all other numeric types in Python are immutable, Rational objects will be immutable. Internally, the representation will insure that the numerator and denominator have a greatest common divisor of 1, and that the sign of the denominator is positive. Implicit Construction --------------------- Rationals will mix with integer types. If the other operand is not rational, it will be converted to rational before the opeation is performed. When combined with a floating type - either complex or float - or a decimal type, the result will be a TypeError. The reason for this is that floating point numbers - including complex - and decimals are already imprecise. To convert them to rational would give an incorrect impression that the results of the operation are precise. The proper way to add a rational to one of these types is to convert the rational to that type explicitly before doing the operation. Operations ---------- The ``Rational`` class shall define all the standard mathematical operations mentioned in the ''Python Usability'' section. Rationals can be converted to floats by float(rational), and to integers by int(rational). int(rational) will just do an integer division of the numerator by the denominator. If there is not a __decimal__ feature for objects in Python 2.5, the rational type will provide a decimal() method that returns the value of self converted to a decimal in the current context. Exceptions ---------- The module will define and at times raise the following exceptions: - DivisionByZero: divide by zero. - OverflowError: overflow attempting to convert to a float. - TypeError: trying to create a rational from a non-integer or non-string type, or trying to perform an operation with a float, complex or decimal. - ValueError: trying to create a rational from a string value that is not a valid represetnation of an integer or floating point number. Note that the decimal initializer will have to be modified to handle rationals. Open Issues =========== - Should raising a rational to a non-integer rational silently produce a float, or raise an InvalidOperation exception? Implementation ============== There is currently a rational module distributed with Python, and a second rational module in the Python cvs source tree that is not distributed. While one of these could be chosen and made to conform to the specification, I am hoping that several people will volunteer implementatins so that a ''best of breed'' implementation may be chosen. References ========== .. [#PEP-239] Adding a Rational Type to Python, Craig, Zadka (http://www.python.org/peps/pep-0239.html) .. [#PEP-240] Adding a Rational Literal to Python, Craig, Zadka (http://www.python.org/peps/pep-0240.html) .. [#PEP-327] Decimal Data Type, Batista (http://www.python.org/peps/pep-0327.html) .. [#PEP-239-implicit] PEP 240 adds a new literal type to Pytbon, PEP 239 implies that division of integers would change to return rationals. Copyright ========= This document has been placed in the public domain. .. Local Variables: mode: indented-text indent-tabs-mode: nil sentence-end-double-space: t fill-column: 70 End: -- Mike Meyer <[EMAIL PROTECTED]> http://www.mired.org/home/mwm/ Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information. -- http://mail.python.org/mailman/listinfo/python-list