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 <mw*@mired.org>

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 <mw*@mired.org> http://www.mired.org/home/mwm/

Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.