]> granicus.if.org Git - python/commitdiff
Backport PEP 3141 from the py3k branch to the trunk. This includes r50877 (just
authorJeffrey Yasskin <jyasskin@gmail.com>
Thu, 3 Jan 2008 02:21:52 +0000 (02:21 +0000)
committerJeffrey Yasskin <jyasskin@gmail.com>
Thu, 3 Jan 2008 02:21:52 +0000 (02:21 +0000)
the complex_pow part), r56649, r56652, r56715, r57296, r57302, r57359, r57361,
r57372, r57738, r57739, r58017, r58039, r58040, and r59390, and new
documentation. The only significant difference is that round(x) returns a float
to preserve backward-compatibility. See http://bugs.python.org/issue1689.

21 files changed:
Doc/library/functions.rst
Doc/library/math.rst
Doc/library/numbers.rst [new file with mode: 0644]
Doc/library/numeric.rst
Doc/library/stdtypes.rst
Doc/reference/datamodel.rst
Doc/reference/expressions.rst
Lib/numbers.py [new file with mode: 0644]
Lib/test/test_abstract_numbers.py [new file with mode: 0644]
Lib/test/test_builtin.py
Lib/test/test_long.py
Lib/test/test_math.py
Lib/test/test_unittest.py
Lib/unittest.py
Misc/NEWS
Modules/mathmodule.c
Objects/complexobject.c
Objects/floatobject.c
Objects/intobject.c
Objects/longobject.c
Python/bltinmodule.c

index 9c11b6dcc157eb651f217122172c31064e5ee172..3236ccdc512afaae05cc21275d2df8ec16a98207 100644 (file)
@@ -986,10 +986,13 @@ available.  They are listed here in alphabetical order.
 .. function:: round(x[, n])
 
    Return the floating point value *x* rounded to *n* digits after the decimal
-   point.  If *n* is omitted, it defaults to zero. The result is a floating point
-   number.  Values are rounded to the closest multiple of 10 to the power minus
-   *n*; if two multiples are equally close, rounding is done away from 0 (so. for
-   example, ``round(0.5)`` is ``1.0`` and ``round(-0.5)`` is ``-1.0``).
+   point.  If *n* is omitted, it defaults to zero.  Values are rounded to the
+   closest multiple of 10 to the power minus *n*; if two multiples are equally
+   close, rounding is done toward the even choice (so, for example, both
+   ``round(0.5)`` and ``round(-0.5)`` are ``0``, and ``round(1.5)`` is
+   ``2``). Delegates to ``x.__round__(n)``.
+
+   .. versionchanged:: 2.6
 
 
 .. function:: set([iterable])
@@ -1132,6 +1135,14 @@ available.  They are listed here in alphabetical order.
    .. versionadded:: 2.2
 
 
+.. function:: trunc(x)
+
+   Return the :class:`Real` value *x* truncated to an :class:`Integral` (usually
+   a long integer). Delegates to ``x.__trunc__()``.
+
+   .. versionadded:: 2.6
+
+
 .. function:: tuple([iterable])
 
    Return a tuple whose items are the same and in the same order as *iterable*'s
index 17c75d3f8218782af98d3977bfb1c219c3dd706b..f92610f0f0b6a8a59eff10e638c6a70f4a30e13d 100644 (file)
@@ -26,8 +26,9 @@ Number-theoretic and representation functions:
 
 .. function:: ceil(x)
 
-   Return the ceiling of *x* as a float, the smallest integer value greater than or
-   equal to *x*.
+   Return the ceiling of *x* as a float, the smallest integer value greater than
+   or equal to *x*. If *x* is not a float, delegates to ``x.__ceil__()``, which
+   should return an :class:`Integral` value.
 
 
 .. function:: fabs(x)
@@ -37,8 +38,9 @@ Number-theoretic and representation functions:
 
 .. function:: floor(x)
 
-   Return the floor of *x* as a float, the largest integer value less than or equal
-   to *x*.
+   Return the floor of *x* as a float, the largest integer value less than or
+   equal to *x*. If *x* is not a float, delegates to ``x.__floor__()``, which
+   should return an :class:`Integral` value.
 
 
 .. function:: fmod(x, y)
diff --git a/Doc/library/numbers.rst b/Doc/library/numbers.rst
new file mode 100644 (file)
index 0000000..d0f9c3b
--- /dev/null
@@ -0,0 +1,99 @@
+
+:mod:`numbers` --- Numeric abstract base classes
+================================================
+
+.. module:: numbers
+   :synopsis: Numeric abstract base classes (Complex, Real, Integral, etc.).
+
+The :mod:`numbers` module (:pep:`3141`) defines a hierarchy of numeric abstract
+base classes which progressively define more operations. These concepts also
+provide a way to distinguish exact from inexact types. None of the types defined
+in this module can be instantiated.
+
+
+.. class:: Number
+
+   The root of the numeric hierarchy. If you just want to check if an argument
+   *x* is a number, without caring what kind, use ``isinstance(x, Number)``.
+
+
+Exact and inexact operations
+----------------------------
+
+.. class:: Exact
+
+   Subclasses of this type have exact operations.
+
+   As long as the result of a homogenous operation is of the same type, you can
+   assume that it was computed exactly, and there are no round-off errors. Laws
+   like commutativity and associativity hold.
+
+
+.. class:: Inexact
+
+   Subclasses of this type have inexact operations.
+
+   Given X, an instance of :class:`Inexact`, it is possible that ``(X + -X) + 3
+   == 3``, but ``X + (-X + 3) == 0``. The exact form this error takes will vary
+   by type, but it's generally unsafe to compare this type for equality.
+
+
+The numeric tower
+-----------------
+
+.. class:: Complex
+
+   Subclasses of this type describe complex numbers and include the operations
+   that work on the builtin :class:`complex` type. These are: conversions to
+   :class:`complex` and :class:`bool`, :attr:`.real`, :attr:`.imag`, ``+``,
+   ``-``, ``*``, ``/``, :func:`abs`, :meth:`conjugate`, ``==``, and ``!=``. All
+   except ``-`` and ``!=`` are abstract.
+
+.. attribute:: Complex.real
+
+   Abstract. Retrieves the :class:`Real` component of this number.
+
+.. attribute:: Complex.imag
+
+   Abstract. Retrieves the :class:`Real` component of this number.
+
+.. method:: Complex.conjugate()
+
+   Abstract. Returns the complex conjugate. For example, ``(1+3j).conjugate() ==
+   (1-3j)``.
+
+.. class:: Real
+
+   To :class:`Complex`, :class:`Real` adds the operations that work on real
+   numbers.
+
+   In short, those are: a conversion to :class:`float`, :func:`trunc`,
+   :func:`round`, :func:`math.floor`, :func:`math.ceil`, :func:`divmod`, ``//``,
+   ``%``, ``<``, ``<=``, ``>``, and ``>=``.
+
+   Real also provides defaults for :func:`complex`, :attr:`Complex.real`,
+   :attr:`Complex.imag`, and :meth:`Complex.conjugate`.
+
+
+.. class:: Rational
+
+   Subtypes both :class:`Real` and :class:`Exact`, and adds
+   :attr:`Rational.numerator` and :attr:`Rational.denominator` properties, which
+   should be in lowest terms. With these, it provides a default for
+   :func:`float`.
+
+.. attribute:: Rational.numerator
+
+   Abstract.
+
+.. attribute:: Rational.denominator
+
+   Abstract.
+
+
+.. class:: Integral
+
+   Subtypes :class:`Rational` and adds a conversion to :class:`long`, the
+   3-argument form of :func:`pow`, and the bit-string operations: ``<<``,
+   ``>>``, ``&``, ``^``, ``|``, ``~``. Provides defaults for :func:`float`,
+   :attr:`Rational.numerator`, and :attr:`Rational.denominator`.
index 0d9d59fbe16b648d5824ec86ae4bad917358a81d..d2b4d8b346e168716caed0e14c3c8359c3b8fb8d 100644 (file)
@@ -6,16 +6,18 @@ Numeric and Mathematical Modules
 ********************************
 
 The modules described in this chapter provide numeric and math-related functions
-and data types. The :mod:`math` and :mod:`cmath` contain  various mathematical
-functions for floating-point and complex numbers. For users more interested in
-decimal accuracy than in speed, the  :mod:`decimal` module supports exact
-representations of  decimal numbers.
+and data types. The :mod:`numbers` module defines an abstract hierarchy of
+numeric types. The :mod:`math` and :mod:`cmath` modules contain various
+mathematical functions for floating-point and complex numbers. For users more
+interested in decimal accuracy than in speed, the :mod:`decimal` module supports
+exact representations of decimal numbers.
 
 The following modules are documented in this chapter:
 
 
 .. toctree::
 
+   numbers.rst
    math.rst
    cmath.rst
    decimal.rst
index 99c19235c3e04f18b7392daf5fa0e9b42f346686..7352a1d74a8a7b0a2b097bfcbb8252fe9a580013 100644 (file)
@@ -270,9 +270,8 @@ numbers of mixed type use the same rule. [#]_ The constructors :func:`int`,
 :func:`long`, :func:`float`, and :func:`complex` can be used to produce numbers
 of a specific type.
 
-All numeric types (except complex) support the following operations, sorted by
-ascending priority (operations in the same box have the same priority; all
-numeric operations have a higher priority than comparison operations):
+All builtin numeric types support the following operations. See
+:ref:`power` and later sections for the operators' priorities.
 
 +--------------------+---------------------------------+--------+
 | Operation          | Result                          | Notes  |
@@ -285,7 +284,7 @@ numeric operations have a higher priority than comparison operations):
 +--------------------+---------------------------------+--------+
 | ``x / y``          | quotient of *x* and *y*         | \(1)   |
 +--------------------+---------------------------------+--------+
-| ``x // y``         | (floored) quotient of *x* and   | \(5)   |
+| ``x // y``         | (floored) quotient of *x* and   | (4)(5) |
 |                    | *y*                             |        |
 +--------------------+---------------------------------+--------+
 | ``x % y``          | remainder of ``x / y``          | \(4)   |
@@ -294,7 +293,7 @@ numeric operations have a higher priority than comparison operations):
 +--------------------+---------------------------------+--------+
 | ``+x``             | *x* unchanged                   |        |
 +--------------------+---------------------------------+--------+
-| ``abs(x)``         | absolute value or magnitude of  |        |
+| ``abs(x)``         | absolute value or magnitude of  | \(3)   |
 |                    | *x*                             |        |
 +--------------------+---------------------------------+--------+
 | ``int(x)``         | *x* converted to integer        | \(2)   |
@@ -308,11 +307,11 @@ numeric operations have a higher priority than comparison operations):
 |                    | *im* defaults to zero.          |        |
 +--------------------+---------------------------------+--------+
 | ``c.conjugate()``  | conjugate of the complex number |        |
-|                    | *c*                             |        |
+|                    | *c*. (Identity on real numbers) |        |
 +--------------------+---------------------------------+--------+
 | ``divmod(x, y)``   | the pair ``(x // y, x % y)``    | (3)(4) |
 +--------------------+---------------------------------+--------+
-| ``pow(x, y)``      | *x* to the power *y*            |        |
+| ``pow(x, y)``      | *x* to the power *y*            | \(3)   |
 +--------------------+---------------------------------+--------+
 | ``x ** y``         | *x* to the power *y*            |        |
 +--------------------+---------------------------------+--------+
@@ -341,9 +340,12 @@ Notes:
       pair: numeric; conversions
       pair: C; language
 
-   Conversion from floating point to (long or plain) integer may round or truncate
-   as in C; see functions :func:`floor` and :func:`ceil` in the :mod:`math` module
-   for well-defined conversions.
+   Conversion from floating point to (long or plain) integer may round or
+   truncate as in C.
+
+   .. deprecated:: 2.6
+      Instead, convert floats to long explicitly with :func:`trunc`,
+      :func:`math.floor`, or :func:`math.ceil`.
 
 (3)
    See :ref:`built-in-funcs` for a full description.
@@ -364,6 +366,22 @@ Notes:
    
    .. versionadded:: 2.6
 
+All :class:`numbers.Real` types (:class:`int`, :class:`long`, and
+:class:`float`) also include the following operations:
+
++--------------------+--------------------------------+--------+
+| Operation          | Result                         | Notes  |
++====================+================================+========+
+| ``trunc(x)``       | *x* truncated to Integral      |        |
++--------------------+--------------------------------+--------+
+| ``round(x[, n])``  | *x* rounded to n digits,       |        |
+|                    | rounding half to even. If n is |        |
+|                    | omitted, it defaults to 0.     |        |
++--------------------+--------------------------------+--------+
+| ``math.floor(x)``  | the greatest Integral <= *x*   |        |
++--------------------+--------------------------------+--------+
+| ``math.ceil(x)``   | the least Integral >= *x*      |        |
++--------------------+--------------------------------+--------+
 
 .. XXXJH exceptions: overflow (when? what operations?) zerodivision
 
index b45044de580da2a3a0edf472fabe5afb4e903d5a..6fc1f8eb44d19a8540df07af9b12bacb57db9821 100644 (file)
@@ -150,7 +150,7 @@ Ellipsis
    indicate the presence of the ``...`` syntax in a slice.  Its truth value is
    true.
 
-Numbers
+:class:`numbers.Number`
    .. index:: object: numeric
 
    These are created by numeric literals and returned as results by arithmetic
@@ -162,7 +162,7 @@ Numbers
    Python distinguishes between integers, floating point numbers, and complex
    numbers:
 
-   Integers
+   :class:`numbers.Integral`
       .. index:: object: integer
 
       These represent elements from the mathematical set of integers (positive and
@@ -214,7 +214,7 @@ Numbers
       without causing overflow, will yield the same result in the long integer domain
       or when using mixed operands.
 
-   Floating point numbers
+   :class:`numbers.Real` (:class:`float`)
       .. index::
          object: floating point
          pair: floating point; number
@@ -229,7 +229,7 @@ Numbers
       overhead of using objects in Python, so there is no reason to complicate the
       language with two kinds of floating point numbers.
 
-   Complex numbers
+   :class:`numbers.Complex`
       .. index::
          object: complex
          pair: complex; number
index ea2bb1a057dc7749d80efb885ed8edca625c2461..9c416f85963a9f22e8d1c4ef2c4d8f82636c7680 100644 (file)
@@ -801,7 +801,8 @@ were of integer types and the second argument was negative, an exception was
 raised).
 
 Raising ``0.0`` to a negative power results in a :exc:`ZeroDivisionError`.
-Raising a negative number to a fractional power results in a :exc:`ValueError`.
+Raising a negative number to a fractional power results in a :class:`complex`
+number. (Since Python 2.6. In earlier versions it raised a :exc:`ValueError`.)
 
 
 .. _unary:
diff --git a/Lib/numbers.py b/Lib/numbers.py
new file mode 100644 (file)
index 0000000..d23fa34
--- /dev/null
@@ -0,0 +1,393 @@
+# Copyright 2007 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Abstract Base Classes (ABCs) for numbers, according to PEP 3141.
+
+TODO: Fill out more detailed documentation on the operators."""
+
+from abc import ABCMeta, abstractmethod, abstractproperty
+
+__all__ = ["Number", "Exact", "Inexact",
+           "Complex", "Real", "Rational", "Integral",
+           ]
+
+
+class Number(object):
+    """All numbers inherit from this class.
+
+    If you just want to check if an argument x is a number, without
+    caring what kind, use isinstance(x, Number).
+    """
+    __metaclass__ = ABCMeta
+
+
+class Exact(Number):
+    """Operations on instances of this type are exact.
+
+    As long as the result of a homogenous operation is of the same
+    type, you can assume that it was computed exactly, and there are
+    no round-off errors. Laws like commutativity and associativity
+    hold.
+    """
+
+Exact.register(int)
+Exact.register(long)
+
+
+class Inexact(Number):
+    """Operations on instances of this type are inexact.
+
+    Given X, an instance of Inexact, it is possible that (X + -X) + 3
+    == 3, but X + (-X + 3) == 0. The exact form this error takes will
+    vary by type, but it's generally unsafe to compare this type for
+    equality.
+    """
+
+Inexact.register(complex)
+Inexact.register(float)
+# Inexact.register(decimal.Decimal)
+
+
+class Complex(Number):
+    """Complex defines the operations that work on the builtin complex type.
+
+    In short, those are: a conversion to complex, .real, .imag, +, -,
+    *, /, abs(), .conjugate, ==, and !=.
+
+    If it is given heterogenous arguments, and doesn't have special
+    knowledge about them, it should fall back to the builtin complex
+    type as described below.
+    """
+
+    @abstractmethod
+    def __complex__(self):
+        """Return a builtin complex instance. Called for complex(self)."""
+
+    def __bool__(self):
+        """True if self != 0. Called for bool(self)."""
+        return self != 0
+
+    @abstractproperty
+    def real(self):
+        """Retrieve the real component of this number.
+
+        This should subclass Real.
+        """
+        raise NotImplementedError
+
+    @abstractproperty
+    def imag(self):
+        """Retrieve the real component of this number.
+
+        This should subclass Real.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def __add__(self, other):
+        """self + other"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __radd__(self, other):
+        """other + self"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __neg__(self):
+        """-self"""
+        raise NotImplementedError
+
+    def __pos__(self):
+        """+self"""
+        raise NotImplementedError
+
+    def __sub__(self, other):
+        """self - other"""
+        return self + -other
+
+    def __rsub__(self, other):
+        """other - self"""
+        return -self + other
+
+    @abstractmethod
+    def __mul__(self, other):
+        """self * other"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __rmul__(self, other):
+        """other * self"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __div__(self, other):
+        """self / other; should promote to float or complex when necessary."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __rdiv__(self, other):
+        """other / self"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __pow__(self, exponent):
+        """self**exponent; should promote to float or complex when necessary."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __rpow__(self, base):
+        """base ** self"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __abs__(self):
+        """Returns the Real distance from 0. Called for abs(self)."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def conjugate(self):
+        """(x+y*i).conjugate() returns (x-y*i)."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __eq__(self, other):
+        """self == other"""
+        raise NotImplementedError
+
+    # __ne__ is inherited from object and negates whatever __eq__ does.
+
+Complex.register(complex)
+
+
+class Real(Complex):
+    """To Complex, Real adds the operations that work on real numbers.
+
+    In short, those are: a conversion to float, trunc(), divmod,
+    %, <, <=, >, and >=.
+
+    Real also provides defaults for the derived operations.
+    """
+
+    @abstractmethod
+    def __float__(self):
+        """Any Real can be converted to a native float object.
+
+        Called for float(self)."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __trunc__(self):
+        """trunc(self): Truncates self to an Integral.
+
+        Returns an Integral i such that:
+          * i>0 iff self>0;
+          * abs(i) <= abs(self);
+          * for any Integral j satisfying the first two conditions,
+            abs(i) >= abs(j) [i.e. i has "maximal" abs among those].
+        i.e. "truncate towards 0".
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def __floor__(self):
+        """Finds the greatest Integral <= self."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __ceil__(self):
+        """Finds the least Integral >= self."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __round__(self, ndigits=None):
+        """Rounds self to ndigits decimal places, defaulting to 0.
+
+        If ndigits is omitted or None, returns an Integral, otherwise
+        returns a Real. Rounds half toward even.
+        """
+        raise NotImplementedError
+
+    def __divmod__(self, other):
+        """divmod(self, other): The pair (self // other, self % other).
+
+        Sometimes this can be computed faster than the pair of
+        operations.
+        """
+        return (self // other, self % other)
+
+    def __rdivmod__(self, other):
+        """divmod(other, self): The pair (self // other, self % other).
+
+        Sometimes this can be computed faster than the pair of
+        operations.
+        """
+        return (other // self, other % self)
+
+    @abstractmethod
+    def __floordiv__(self, other):
+        """self // other: The floor() of self/other."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __rfloordiv__(self, other):
+        """other // self: The floor() of other/self."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __mod__(self, other):
+        """self % other"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __rmod__(self, other):
+        """other % self"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __lt__(self, other):
+        """self < other
+
+        < on Reals defines a total ordering, except perhaps for NaN."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __le__(self, other):
+        """self <= other"""
+        raise NotImplementedError
+
+    # Concrete implementations of Complex abstract methods.
+    def __complex__(self):
+        """complex(self) == complex(float(self), 0)"""
+        return complex(float(self))
+
+    @property
+    def real(self):
+        """Real numbers are their real component."""
+        return +self
+
+    @property
+    def imag(self):
+        """Real numbers have no imaginary component."""
+        return 0
+
+    def conjugate(self):
+        """Conjugate is a no-op for Reals."""
+        return +self
+
+Real.register(float)
+# Real.register(decimal.Decimal)
+
+
+class Rational(Real, Exact):
+    """.numerator and .denominator should be in lowest terms."""
+
+    @abstractproperty
+    def numerator(self):
+        raise NotImplementedError
+
+    @abstractproperty
+    def denominator(self):
+        raise NotImplementedError
+
+    # Concrete implementation of Real's conversion to float.
+    def __float__(self):
+        """float(self) = self.numerator / self.denominator"""
+        return self.numerator / self.denominator
+
+
+class Integral(Rational):
+    """Integral adds a conversion to long and the bit-string operations."""
+
+    @abstractmethod
+    def __long__(self):
+        """long(self)"""
+        raise NotImplementedError
+
+    def __index__(self):
+        """index(self)"""
+        return long(self)
+
+    @abstractmethod
+    def __pow__(self, exponent, modulus=None):
+        """self ** exponent % modulus, but maybe faster.
+
+        Accept the modulus argument if you want to support the
+        3-argument version of pow(). Raise a TypeError if exponent < 0
+        or any argument isn't Integral. Otherwise, just implement the
+        2-argument version described in Complex.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def __lshift__(self, other):
+        """self << other"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __rlshift__(self, other):
+        """other << self"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __rshift__(self, other):
+        """self >> other"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __rrshift__(self, other):
+        """other >> self"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __and__(self, other):
+        """self & other"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __rand__(self, other):
+        """other & self"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __xor__(self, other):
+        """self ^ other"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __rxor__(self, other):
+        """other ^ self"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __or__(self, other):
+        """self | other"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __ror__(self, other):
+        """other | self"""
+        raise NotImplementedError
+
+    @abstractmethod
+    def __invert__(self):
+        """~self"""
+        raise NotImplementedError
+
+    # Concrete implementations of Rational and Real abstract methods.
+    def __float__(self):
+        """float(self) == float(long(self))"""
+        return float(long(self))
+
+    @property
+    def numerator(self):
+        """Integers are their own numerators."""
+        return +self
+
+    @property
+    def denominator(self):
+        """Integers have a denominator of 1."""
+        return 1
+
+Integral.register(int)
+Integral.register(long)
diff --git a/Lib/test/test_abstract_numbers.py b/Lib/test/test_abstract_numbers.py
new file mode 100644 (file)
index 0000000..19bfc79
--- /dev/null
@@ -0,0 +1,62 @@
+"""Unit tests for numbers.py."""
+
+import unittest
+from test import test_support
+from numbers import Number
+from numbers import Exact, Inexact
+from numbers import Complex, Real, Rational, Integral
+import operator
+
+class TestNumbers(unittest.TestCase):
+    def test_int(self):
+        self.failUnless(issubclass(int, Integral))
+        self.failUnless(issubclass(int, Complex))
+        self.failUnless(issubclass(int, Exact))
+        self.failIf(issubclass(int, Inexact))
+
+        self.assertEqual(7, int(7).real)
+        self.assertEqual(0, int(7).imag)
+        self.assertEqual(7, int(7).conjugate())
+        self.assertEqual(7, int(7).numerator)
+        self.assertEqual(1, int(7).denominator)
+
+    def test_long(self):
+        self.failUnless(issubclass(long, Integral))
+        self.failUnless(issubclass(long, Complex))
+        self.failUnless(issubclass(long, Exact))
+        self.failIf(issubclass(long, Inexact))
+
+        self.assertEqual(7, long(7).real)
+        self.assertEqual(0, long(7).imag)
+        self.assertEqual(7, long(7).conjugate())
+        self.assertEqual(7, long(7).numerator)
+        self.assertEqual(1, long(7).denominator)
+
+    def test_float(self):
+        self.failIf(issubclass(float, Rational))
+        self.failUnless(issubclass(float, Real))
+        self.failIf(issubclass(float, Exact))
+        self.failUnless(issubclass(float, Inexact))
+
+        self.assertEqual(7.3, float(7.3).real)
+        self.assertEqual(0, float(7.3).imag)
+        self.assertEqual(7.3, float(7.3).conjugate())
+
+    def test_complex(self):
+        self.failIf(issubclass(complex, Real))
+        self.failUnless(issubclass(complex, Complex))
+        self.failIf(issubclass(complex, Exact))
+        self.failUnless(issubclass(complex, Inexact))
+
+        c1, c2 = complex(3, 2), complex(4,1)
+        # XXX: This is not ideal, but see the comment in builtin_trunc().
+        self.assertRaises(AttributeError, trunc, c1)
+        self.assertRaises(TypeError, float, c1)
+        self.assertRaises(TypeError, int, c1)
+
+def test_main():
+    test_support.run_unittest(TestNumbers)
+
+
+if __name__ == "__main__":
+    unittest.main()
index 52178c8b64cbb9a270529cb39a78dcda5664339a..c5a1cc3fb214a33890d0c11551fb63878587b36f 100644 (file)
@@ -1450,11 +1450,13 @@ class BuiltinTest(unittest.TestCase):
                     else:
                         self.assertAlmostEqual(pow(x, y, z), 24.0)
 
+        self.assertAlmostEqual(pow(-1, 0.5), 1j)
+        self.assertAlmostEqual(pow(-1, 1./3), 0.5 + 0.8660254037844386j)
+
         self.assertRaises(TypeError, pow, -1, -2, 3)
         self.assertRaises(ValueError, pow, 1, 2, 0)
         self.assertRaises(TypeError, pow, -1L, -2L, 3L)
         self.assertRaises(ValueError, pow, 1L, 2L, 0L)
-        self.assertRaises(ValueError, pow, -342.43, 0.234)
 
         self.assertRaises(TypeError, pow)
 
@@ -1622,6 +1624,7 @@ class BuiltinTest(unittest.TestCase):
 
     def test_round(self):
         self.assertEqual(round(0.0), 0.0)
+        self.assertEqual(type(round(0.0)), float)  # Will be int in 3.0.
         self.assertEqual(round(1.0), 1.0)
         self.assertEqual(round(10.0), 10.0)
         self.assertEqual(round(1000000000.0), 1000000000.0)
@@ -1650,12 +1653,50 @@ class BuiltinTest(unittest.TestCase):
         self.assertEqual(round(-999999999.9), -1000000000.0)
 
         self.assertEqual(round(-8.0, -1), -10.0)
+        self.assertEqual(type(round(-8.0, -1)), float)
+
+        self.assertEqual(type(round(-8.0, 0)), float)
+        self.assertEqual(type(round(-8.0, 1)), float)
+
+        # Check even / odd rounding behaviour
+        self.assertEqual(round(5.5), 6)
+        self.assertEqual(round(6.5), 6)
+        self.assertEqual(round(-5.5), -6)
+        self.assertEqual(round(-6.5), -6)
+
+        # Check behavior on ints
+        self.assertEqual(round(0), 0)
+        self.assertEqual(round(8), 8)
+        self.assertEqual(round(-8), -8)
+        self.assertEqual(type(round(0)), float)  # Will be int in 3.0.
+        self.assertEqual(type(round(-8, -1)), float)
+        self.assertEqual(type(round(-8, 0)), float)
+        self.assertEqual(type(round(-8, 1)), float)
 
         # test new kwargs
         self.assertEqual(round(number=-8.0, ndigits=-1), -10.0)
 
         self.assertRaises(TypeError, round)
 
+        # test generic rounding delegation for reals
+        class TestRound(object):
+            def __round__(self):
+                return 23
+
+        class TestNoRound(object):
+            pass
+
+        self.assertEqual(round(TestRound()), 23)
+
+        self.assertRaises(TypeError, round, 1, 2, 3)
+        # XXX: This is not ideal, but see the comment in builtin_round().
+        self.assertRaises(AttributeError, round, TestNoRound())
+
+        t = TestNoRound()
+        t.__round__ = lambda *args: args
+        self.assertEquals((), round(t))
+        self.assertEquals((0,), round(t, 0))
+
     def test_setattr(self):
         setattr(sys, 'spam', 1)
         self.assertEqual(sys.spam, 1)
@@ -1697,6 +1738,38 @@ class BuiltinTest(unittest.TestCase):
                 raise ValueError
         self.assertRaises(ValueError, sum, BadSeq())
 
+    def test_trunc(self):
+
+        self.assertEqual(trunc(1), 1)
+        self.assertEqual(trunc(-1), -1)
+        self.assertEqual(type(trunc(1)), int)
+        self.assertEqual(type(trunc(1.5)), int)
+        self.assertEqual(trunc(1.5), 1)
+        self.assertEqual(trunc(-1.5), -1)
+        self.assertEqual(trunc(1.999999), 1)
+        self.assertEqual(trunc(-1.999999), -1)
+        self.assertEqual(trunc(-0.999999), -0)
+        self.assertEqual(trunc(-100.999), -100)
+
+        class TestTrunc(object):
+            def __trunc__(self):
+                return 23
+
+        class TestNoTrunc(object):
+            pass
+
+        self.assertEqual(trunc(TestTrunc()), 23)
+
+        self.assertRaises(TypeError, trunc)
+        self.assertRaises(TypeError, trunc, 1, 2)
+        # XXX: This is not ideal, but see the comment in builtin_trunc().
+        self.assertRaises(AttributeError, trunc, TestNoTrunc())
+
+        t = TestNoTrunc()
+        t.__trunc__ = lambda *args: args
+        self.assertEquals((), trunc(t))
+        self.assertRaises(TypeError, trunc, t, 0)
+
     def test_tuple(self):
         self.assertEqual(tuple(()), ())
         t0_3 = (0, 1, 2, 3)
index ae132adad93b2b3237846794e168d3bde93b502a..29515c7bb70c576e5fcba8ad9ea58f357b56eb58 100644 (file)
@@ -385,7 +385,9 @@ class LongTest(unittest.TestCase):
                      "1. ** huge", "huge ** 1.", "1. ** mhuge", "mhuge ** 1.",
                      "math.sin(huge)", "math.sin(mhuge)",
                      "math.sqrt(huge)", "math.sqrt(mhuge)", # should do better
-                     "math.floor(huge)", "math.floor(mhuge)"]:
+                     # math.floor() of an int returns an int now
+                     ##"math.floor(huge)", "math.floor(mhuge)",
+                     ]:
 
             self.assertRaises(OverflowError, eval, test, namespace)
 
index d86298d01bd80a4f6c51b6f06ab11a9a142c0f29..98e4623c2988c16dc070368ff183646c68cee329 100644 (file)
@@ -58,6 +58,19 @@ class MathTests(unittest.TestCase):
         self.ftest('ceil(-1.0)', math.ceil(-1.0), -1)
         self.ftest('ceil(-1.5)', math.ceil(-1.5), -1)
 
+        class TestCeil(object):
+            def __ceil__(self):
+                return 42
+        class TestNoCeil(object):
+            pass
+        self.ftest('ceil(TestCeil())', math.ceil(TestCeil()), 42)
+        self.assertRaises(TypeError, math.ceil, TestNoCeil())
+
+        t = TestNoCeil()
+        t.__ceil__ = lambda *args: args
+        self.assertRaises(TypeError, math.ceil, t)
+        self.assertRaises(TypeError, math.ceil, t, 0)
+
     def testCos(self):
         self.assertRaises(TypeError, math.cos)
         self.ftest('cos(-pi/2)', math.cos(-math.pi/2), 0)
@@ -101,6 +114,19 @@ class MathTests(unittest.TestCase):
         self.ftest('floor(1.23e167)', math.floor(1.23e167), 1.23e167)
         self.ftest('floor(-1.23e167)', math.floor(-1.23e167), -1.23e167)
 
+        class TestFloor(object):
+            def __floor__(self):
+                return 42
+        class TestNoFloor(object):
+            pass
+        self.ftest('floor(TestFloor())', math.floor(TestFloor()), 42)
+        self.assertRaises(TypeError, math.floor, TestNoFloor())
+
+        t = TestNoFloor()
+        t.__floor__ = lambda *args: args
+        self.assertRaises(TypeError, math.floor, t)
+        self.assertRaises(TypeError, math.floor, t, 0)
+
     def testFmod(self):
         self.assertRaises(TypeError, math.fmod)
         self.ftest('fmod(10,1)', math.fmod(10,1), 0)
index 9dfed7b58c5590dbcbda42526b7f4a0a81c85cc3..5a827805b194aa6a50d38307ff0061a8acb92fbc 100644 (file)
@@ -2264,13 +2264,34 @@ class Test_TestCase(TestCase, TestEquality, TestHashing):
         expected = ['startTest', 'test', 'stopTest']
         self.assertEqual(events, expected)
 
+class Test_Assertions(TestCase):
+    def test_AlmostEqual(self):
+        self.failUnlessAlmostEqual(1.00000001, 1.0)
+        self.failIfAlmostEqual(1.0000001, 1.0)
+        self.assertRaises(AssertionError,
+                          self.failUnlessAlmostEqual, 1.0000001, 1.0)
+        self.assertRaises(AssertionError,
+                          self.failIfAlmostEqual, 1.00000001, 1.0)
+
+        self.failUnlessAlmostEqual(1.1, 1.0, places=0)
+        self.assertRaises(AssertionError,
+                          self.failUnlessAlmostEqual, 1.1, 1.0, places=1)
+
+        self.failUnlessAlmostEqual(0, .1+.1j, places=0)
+        self.failIfAlmostEqual(0, .1+.1j, places=1)
+        self.assertRaises(AssertionError,
+                          self.failUnlessAlmostEqual, 0, .1+.1j, places=1)
+        self.assertRaises(AssertionError,
+                          self.failIfAlmostEqual, 0, .1+.1j, places=0)
+
 ######################################################################
 ## Main
 ######################################################################
 
 def test_main():
     test_support.run_unittest(Test_TestCase, Test_TestLoader,
-        Test_TestSuite, Test_TestResult, Test_FunctionTestCase)
+        Test_TestSuite, Test_TestResult, Test_FunctionTestCase,
+        Test_Assertions)
 
 if __name__ == "__main__":
     test_main()
index 7ea240c6af956ce71028b566b6606dc131910772..eccefe6026344abcd9e35fbf2c73f901dff93829 100644 (file)
@@ -358,7 +358,7 @@ class TestCase:
            Note that decimal places (from zero) are usually not the same
            as significant digits (measured from the most signficant digit).
         """
-        if round(second-first, places) != 0:
+        if round(abs(second-first), places) != 0:
             raise self.failureException, \
                   (msg or '%r != %r within %r places' % (first, second, places))
 
@@ -370,7 +370,7 @@ class TestCase:
            Note that decimal places (from zero) are usually not the same
            as significant digits (measured from the most signficant digit).
         """
-        if round(second-first, places) == 0:
+        if round(abs(second-first), places) == 0:
             raise self.failureException, \
                   (msg or '%r == %r within %r places' % (first, second, places))
 
index 608982e3738f63322699168ddc900e45061b6652..6aad4d81d4147f6cac0001a97eea2788f1f9e776 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -346,6 +346,8 @@ Core and builtins
 Library
 -------
 
+- Issue #1689: PEP 3141, numeric abstract base classes.
+
 - Tk issue #1851526: Return results from Python callbacks to Tcl as
   Tcl objects.
 
index 14e6bfc8a921331366e14a938f9df1269a3a978a..431590c01d1d469fdf61fa65a73a7247812dae93 100644 (file)
@@ -107,9 +107,28 @@ FUNC1(atan, atan,
 FUNC2(atan2, atan2,
       "atan2(y, x)\n\nReturn the arc tangent (measured in radians) of y/x.\n"
       "Unlike atan(y/x), the signs of both x and y are considered.")
-FUNC1(ceil, ceil,
-      "ceil(x)\n\nReturn the ceiling of x as a float.\n"
-      "This is the smallest integral value >= x.")
+
+static PyObject * math_ceil(PyObject *self, PyObject *number) {
+       static PyObject *ceil_str = NULL;
+       PyObject *method;
+
+       if (ceil_str == NULL) {
+               ceil_str = PyString_FromString("__ceil__");
+               if (ceil_str == NULL)
+                       return NULL;
+       }
+
+       method = _PyType_Lookup(Py_Type(number), ceil_str);
+       if (method == NULL)
+               return math_1(number, ceil);
+       else
+               return PyObject_CallFunction(method, "O", number);
+}
+
+PyDoc_STRVAR(math_ceil_doc,
+            "ceil(x)\n\nReturn the ceiling of x as a float.\n"
+            "This is the smallest integral value >= x.");
+
 FUNC1(cos, cos,
       "cos(x)\n\nReturn the cosine of x (measured in radians).")
 FUNC1(cosh, cosh,
@@ -118,9 +137,28 @@ FUNC1(exp, exp,
       "exp(x)\n\nReturn e raised to the power of x.")
 FUNC1(fabs, fabs,
       "fabs(x)\n\nReturn the absolute value of the float x.")
-FUNC1(floor, floor,
-      "floor(x)\n\nReturn the floor of x as a float.\n"
-      "This is the largest integral value <= x.")
+
+static PyObject * math_floor(PyObject *self, PyObject *number) {
+       static PyObject *floor_str = NULL;
+       PyObject *method;
+
+       if (floor_str == NULL) {
+               floor_str = PyString_FromString("__floor__");
+               if (floor_str == NULL)
+                       return NULL;
+       }
+
+       method = _PyType_Lookup(Py_Type(number), floor_str);
+       if (method == NULL)
+               return math_1(number, floor);
+       else
+               return PyObject_CallFunction(method, "O", number);
+}
+
+PyDoc_STRVAR(math_floor_doc,
+            "floor(x)\n\nReturn the floor of x as a float.\n"
+            "This is the largest integral value <= x.");
+
 FUNC2(fmod, fmod,
       "fmod(x,y)\n\nReturn fmod(x, y), according to platform C."
       "  x % y may differ.")
index 634a753cece2e565575f10af18f37a32d51f14d6..285c1d47927bac4976e90e4b5d36cd0208647a20 100644 (file)
@@ -385,6 +385,41 @@ complex_hash(PyComplexObject *v)
        return combined;
 }
 
+/* This macro may return! */
+#define TO_COMPLEX(obj, c) \
+       if (PyComplex_Check(obj)) \
+               c = ((PyComplexObject *)(obj))->cval; \
+       else if (to_complex(&(obj), &(c)) < 0) \
+               return (obj)
+
+static int
+to_complex(PyObject **pobj, Py_complex *pc)
+{
+    PyObject *obj = *pobj;
+
+    pc->real = pc->imag = 0.0;
+    if (PyInt_Check(obj)) {
+        pc->real = PyInt_AS_LONG(obj);
+        return 0;
+    }
+    if (PyLong_Check(obj)) {
+        pc->real = PyLong_AsDouble(obj);
+        if (pc->real == -1.0 && PyErr_Occurred()) {
+            *pobj = NULL;
+            return -1;
+        }
+        return 0;
+    }
+    if (PyFloat_Check(obj)) {
+        pc->real = PyFloat_AsDouble(obj);
+        return 0;
+    }
+    Py_INCREF(Py_NotImplemented);
+    *pobj = Py_NotImplemented;
+    return -1;
+}
+               
+
 static PyObject *
 complex_add(PyComplexObject *v, PyComplexObject *w)
 {
@@ -502,24 +537,27 @@ complex_divmod(PyComplexObject *v, PyComplexObject *w)
 }
 
 static PyObject *
-complex_pow(PyComplexObject *v, PyObject *w, PyComplexObject *z)
+complex_pow(PyObject *v, PyObject *w, PyObject *z)
 {
        Py_complex p;
        Py_complex exponent;
        long int_exponent;
+       Py_complex a, b;
+        TO_COMPLEX(v, a);
+        TO_COMPLEX(w, b);
 
-       if ((PyObject *)z!=Py_None) {
+       if (z!=Py_None) {
                PyErr_SetString(PyExc_ValueError, "complex modulo");
                return NULL;
        }
        PyFPE_START_PROTECT("complex_pow", return 0)
        errno = 0;
-       exponent = ((PyComplexObject*)w)->cval;
+       exponent = b;
        int_exponent = (long)exponent.real;
        if (exponent.imag == 0. && exponent.real == int_exponent)
-               p = c_powi(v->cval,int_exponent);
+               p = c_powi(a,int_exponent);
        else
-               p = c_pow(v->cval,exponent);
+               p = c_pow(a,exponent);
 
        PyFPE_END_PROTECT(p)
        Py_ADJUST_ERANGE2(p.real, p.imag);
@@ -541,6 +579,10 @@ complex_int_div(PyComplexObject *v, PyComplexObject *w)
 {
        PyObject *t, *r;
        
+       if (PyErr_Warn(PyExc_DeprecationWarning,
+                      "complex divmod(), // and % are deprecated") < 0)
+               return NULL;
+
        t = complex_divmod(v, w);
        if (t != NULL) {
                r = PyTuple_GET_ITEM(t, 0);
@@ -695,6 +737,11 @@ complex_conjugate(PyObject *self)
        return PyComplex_FromCComplex(c);
 }
 
+PyDoc_STRVAR(complex_conjugate_doc,
+"complex.conjugate() -> complex\n"
+"\n"
+"Returns the complex conjugate of its argument. (3-4j).conjugate() == 3+4j.");
+
 static PyObject *
 complex_getnewargs(PyComplexObject *v)
 {
@@ -702,7 +749,8 @@ complex_getnewargs(PyComplexObject *v)
 }
 
 static PyMethodDef complex_methods[] = {
-       {"conjugate",   (PyCFunction)complex_conjugate, METH_NOARGS},
+       {"conjugate",   (PyCFunction)complex_conjugate, METH_NOARGS,
+        complex_conjugate_doc},
        {"__getnewargs__",      (PyCFunction)complex_getnewargs,        METH_NOARGS},
        {NULL,          NULL}           /* sentinel */
 };
index 5d0b920f5c9405c4afc1122ca04591d4f2f9b914..574b96a4e74ae74b54369f2761c1c8c814f4ecd4 100644 (file)
@@ -986,9 +986,10 @@ float_pow(PyObject *v, PyObject *w, PyObject *z)
                 * bugs so we have to figure it out ourselves.
                 */
                if (iw != floor(iw)) {
-                       PyErr_SetString(PyExc_ValueError, "negative number "
-                               "cannot be raised to a fractional power");
-                       return NULL;
+                       /* Negative numbers raised to fractional powers
+                        * become complex.
+                        */
+                       return PyComplex_Type.tp_as_number->nb_power(v, w, z);
                }
                /* iw is an exact integer, albeit perhaps a very large one.
                 * -1 raised to an exact integer should never be exceptional.
@@ -1034,17 +1035,6 @@ float_neg(PyFloatObject *v)
        return PyFloat_FromDouble(-v->ob_fval);
 }
 
-static PyObject *
-float_pos(PyFloatObject *v)
-{
-       if (PyFloat_CheckExact(v)) {
-               Py_INCREF(v);
-               return (PyObject *)v;
-       }
-       else
-               return PyFloat_FromDouble(v->ob_fval);
-}
-
 static PyObject *
 float_abs(PyFloatObject *v)
 {
@@ -1083,14 +1073,7 @@ float_coerce(PyObject **pv, PyObject **pw)
 }
 
 static PyObject *
-float_long(PyObject *v)
-{
-       double x = PyFloat_AsDouble(v);
-       return PyLong_FromDouble(x);
-}
-
-static PyObject *
-float_int(PyObject *v)
+float_trunc(PyObject *v)
 {
        double x = PyFloat_AsDouble(v);
        double wholepart;       /* integral portion of x, rounded toward 0 */
@@ -1115,6 +1098,54 @@ float_int(PyObject *v)
        return PyLong_FromDouble(wholepart);
 }
 
+static PyObject *
+float_round(PyObject *v, PyObject *args)
+{
+#define UNDEF_NDIGITS (-0x7fffffff) /* Unlikely ndigits value */
+       double x;
+       double f;
+       double flr, cil;
+       double rounded;
+       int i;
+       int ndigits = UNDEF_NDIGITS;
+
+       if (!PyArg_ParseTuple(args, "|i", &ndigits))
+               return NULL;
+
+       x = PyFloat_AsDouble(v);
+
+       if (ndigits != UNDEF_NDIGITS) {
+               f = 1.0;
+               i = abs(ndigits);
+               while  (--i >= 0)
+                       f = f*10.0;
+               if (ndigits < 0)
+                       x /= f;
+               else
+                       x *= f;
+       }
+
+       flr = floor(x);
+       cil = ceil(x);
+
+       if (x-flr > 0.5)
+               rounded = cil;
+       else if (x-flr == 0.5) 
+               rounded = fmod(flr, 2) == 0 ? flr : cil;
+       else
+               rounded = flr;
+
+       if (ndigits != UNDEF_NDIGITS) {
+               if (ndigits < 0)
+                       rounded *= f;
+               else
+                       rounded /= f;
+       }
+
+       return PyFloat_FromDouble(rounded);
+#undef UNDEF_NDIGITS
+}
+
 static PyObject *
 float_float(PyObject *v)
 {
@@ -1302,7 +1333,20 @@ PyDoc_STRVAR(float_setformat_doc,
 "Overrides the automatic determination of C-level floating point type.\n"
 "This affects how floats are converted to and from binary strings.");
 
+static PyObject *
+float_getzero(PyObject *v, void *closure)
+{
+       return PyFloat_FromDouble(0.0);
+}
+
 static PyMethodDef float_methods[] = {
+       {"conjugate",   (PyCFunction)float_float,       METH_NOARGS,
+        "Returns self, the complex conjugate of any float."},
+       {"__trunc__",   (PyCFunction)float_trunc, METH_NOARGS,
+         "Returns the Integral closest to x between 0 and x."},
+       {"__round__",   (PyCFunction)float_round, METH_VARARGS,
+         "Returns the Integral closest to x, rounding half toward even.\n"
+         "When an argument is passed, works like built-in round(x, ndigits)."},
        {"__getnewargs__",      (PyCFunction)float_getnewargs,  METH_NOARGS},
        {"__getformat__",       (PyCFunction)float_getformat,   
         METH_O|METH_CLASS,             float_getformat_doc},
@@ -1311,6 +1355,18 @@ static PyMethodDef float_methods[] = {
        {NULL,          NULL}           /* sentinel */
 };
 
+static PyGetSetDef float_getset[] = {
+    {"real", 
+     (getter)float_float, (setter)NULL,
+     "the real part of a complex number",
+     NULL},
+    {"imag", 
+     (getter)float_getzero, (setter)NULL,
+     "the imaginary part of a complex number",
+     NULL},
+    {NULL}  /* Sentinel */
+};
+
 PyDoc_STRVAR(float_doc,
 "float(x) -> floating point number\n\
 \n\
@@ -1326,7 +1382,7 @@ static PyNumberMethods float_as_number = {
        float_divmod,   /*nb_divmod*/
        float_pow,      /*nb_power*/
        (unaryfunc)float_neg, /*nb_negative*/
-       (unaryfunc)float_pos, /*nb_positive*/
+       (unaryfunc)float_float, /*nb_positive*/
        (unaryfunc)float_abs, /*nb_absolute*/
        (inquiry)float_nonzero, /*nb_nonzero*/
        0,              /*nb_invert*/
@@ -1336,8 +1392,8 @@ static PyNumberMethods float_as_number = {
        0,              /*nb_xor*/
        0,              /*nb_or*/
        float_coerce,   /*nb_coerce*/
-       float_int,      /*nb_int*/
-       float_long,     /*nb_long*/
+       float_trunc,    /*nb_int*/
+       float_trunc,    /*nb_long*/
        float_float,    /*nb_float*/
        0,              /* nb_oct */
        0,              /* nb_hex */
@@ -1389,7 +1445,7 @@ PyTypeObject PyFloat_Type = {
        0,                                      /* tp_iternext */
        float_methods,                          /* tp_methods */
        0,                                      /* tp_members */
-       0,                                      /* tp_getset */
+       float_getset,                           /* tp_getset */
        0,                                      /* tp_base */
        0,                                      /* tp_dict */
        0,                                      /* tp_descr_get */
index 0f11974e75a675b90e44db281bb533a9531b46ab..a93b9b201a43559ac54f2e86a1c2ff6e24833256 100644 (file)
@@ -4,6 +4,8 @@
 #include "Python.h"
 #include <ctype.h>
 
+static PyObject *int_int(PyIntObject *v);
+
 long
 PyInt_GetMax(void)
 {
@@ -782,22 +784,11 @@ int_neg(PyIntObject *v)
        return PyInt_FromLong(-a);
 }
 
-static PyObject *
-int_pos(PyIntObject *v)
-{
-       if (PyInt_CheckExact(v)) {
-               Py_INCREF(v);
-               return (PyObject *)v;
-       }
-       else
-               return PyInt_FromLong(v->ob_ival);
-}
-
 static PyObject *
 int_abs(PyIntObject *v)
 {
        if (v->ob_ival >= 0)
-               return int_pos(v);
+               return int_int(v);
        else
                return int_neg(v);
 }
@@ -827,7 +818,7 @@ int_lshift(PyIntObject *v, PyIntObject *w)
                return NULL;
        }
        if (a == 0 || b == 0)
-               return int_pos(v);
+               return int_int(v);
        if (b >= LONG_BIT) {
                vv = PyLong_FromLong(PyInt_AS_LONG(v));
                if (vv == NULL)
@@ -871,7 +862,7 @@ int_rshift(PyIntObject *v, PyIntObject *w)
                return NULL;
        }
        if (a == 0 || b == 0)
-               return int_pos(v);
+               return int_int(v);
        if (b >= LONG_BIT) {
                if (a < 0)
                        a = -1;
@@ -1060,11 +1051,72 @@ int_getnewargs(PyIntObject *v)
        return Py_BuildValue("(l)", v->ob_ival);
 }
 
+static PyObject *
+int_getN(PyIntObject *v, void *context) {
+       return PyInt_FromLong((intptr_t)context);
+}
+
+static PyObject *
+int_round(PyObject *self, PyObject *args)
+{
+#define UNDEF_NDIGITS (-0x7fffffff) /* Unlikely ndigits value */
+       int ndigits = UNDEF_NDIGITS;
+       double x;
+       PyObject *res;
+       
+       if (!PyArg_ParseTuple(args, "|i", &ndigits))
+               return NULL;
+
+       if (ndigits == UNDEF_NDIGITS)
+          return int_float((PyIntObject *)self);
+
+       /* If called with two args, defer to float.__round__(). */
+       x = (double) PyInt_AS_LONG(self);
+       self = PyFloat_FromDouble(x);
+       if (self == NULL)
+               return NULL;
+       res = PyObject_CallMethod(self, "__round__", "i", ndigits);
+       Py_DECREF(self);
+       return res;
+#undef UNDEF_NDIGITS
+}
+
 static PyMethodDef int_methods[] = {
+       {"conjugate",   (PyCFunction)int_int,   METH_NOARGS,
+        "Returns self, the complex conjugate of any int."},
+       {"__trunc__",   (PyCFunction)int_int,   METH_NOARGS,
+         "Truncating an Integral returns itself."},
+       {"__floor__",   (PyCFunction)int_int,   METH_NOARGS,
+         "Flooring an Integral returns itself."},
+       {"__ceil__",    (PyCFunction)int_int,   METH_NOARGS,
+         "Ceiling of an Integral returns itself."},
+       {"__round__",   (PyCFunction)int_round, METH_VARARGS,
+         "Rounding an Integral returns itself.\n"
+        "Rounding with an ndigits arguments defers to float.__round__."},
        {"__getnewargs__",      (PyCFunction)int_getnewargs,    METH_NOARGS},
        {NULL,          NULL}           /* sentinel */
 };
 
+static PyGetSetDef int_getset[] = {
+       {"real", 
+        (getter)int_int, (setter)NULL,
+        "the real part of a complex number",
+        NULL},
+       {"imag", 
+        (getter)int_getN, (setter)NULL,
+        "the imaginary part of a complex number",
+        (void*)0},
+       {"numerator", 
+        (getter)int_int, (setter)NULL,
+        "the numerator of a rational number in lowest terms",
+        NULL},
+       {"denominator", 
+        (getter)int_getN, (setter)NULL,
+        "the denominator of a rational number in lowest terms",
+        (void*)1},
+       {NULL}  /* Sentinel */
+};
+
 PyDoc_STRVAR(int_doc,
 "int(x[, base]) -> integer\n\
 \n\
@@ -1085,7 +1137,7 @@ static PyNumberMethods int_as_number = {
        (binaryfunc)int_divmod, /*nb_divmod*/
        (ternaryfunc)int_pow,   /*nb_power*/
        (unaryfunc)int_neg,     /*nb_negative*/
-       (unaryfunc)int_pos,     /*nb_positive*/
+       (unaryfunc)int_int,     /*nb_positive*/
        (unaryfunc)int_abs,     /*nb_absolute*/
        (inquiry)int_nonzero,   /*nb_nonzero*/
        (unaryfunc)int_invert,  /*nb_invert*/
@@ -1149,7 +1201,7 @@ PyTypeObject PyInt_Type = {
        0,                                      /* tp_iternext */
        int_methods,                            /* tp_methods */
        0,                                      /* tp_members */
-       0,                                      /* tp_getset */
+       int_getset,                             /* tp_getset */
        0,                                      /* tp_base */
        0,                                      /* tp_dict */
        0,                                      /* tp_descr_get */
index 85e862a79c55bb05a880d17ab38000cfed478369..262b40ade123cecee4e9047f55a4b25d85ed1c53 100644 (file)
@@ -1716,7 +1716,7 @@ PyLong_FromUnicode(Py_UNICODE *u, Py_ssize_t length, int base)
 /* forward */
 static PyLongObject *x_divrem
        (PyLongObject *, PyLongObject *, PyLongObject **);
-static PyObject *long_pos(PyLongObject *);
+static PyObject *long_long(PyObject *v);
 static int long_divrem(PyLongObject *, PyLongObject *,
        PyLongObject **, PyLongObject **);
 
@@ -2905,17 +2905,6 @@ long_invert(PyLongObject *v)
        return (PyObject *)x;
 }
 
-static PyObject *
-long_pos(PyLongObject *v)
-{
-       if (PyLong_CheckExact(v)) {
-               Py_INCREF(v);
-               return (PyObject *)v;
-       }
-       else
-               return _PyLong_Copy(v);
-}
-
 static PyObject *
 long_neg(PyLongObject *v)
 {
@@ -2937,7 +2926,7 @@ long_abs(PyLongObject *v)
        if (v->ob_size < 0)
                return long_neg(v);
        else
-               return long_pos(v);
+               return long_long((PyObject *)v);
 }
 
 static int
@@ -3373,11 +3362,74 @@ long_getnewargs(PyLongObject *v)
        return Py_BuildValue("(N)", _PyLong_Copy(v));
 }
 
+static PyObject *
+long_getN(PyLongObject *v, void *context) {
+       return PyLong_FromLong((intptr_t)context);
+}
+
+static PyObject *
+long_round(PyObject *self, PyObject *args)
+{
+#define UNDEF_NDIGITS (-0x7fffffff) /* Unlikely ndigits value */
+       int ndigits = UNDEF_NDIGITS;
+       double x;
+       PyObject *res;
+       
+       if (!PyArg_ParseTuple(args, "|i", &ndigits))
+               return NULL;
+
+       if (ndigits == UNDEF_NDIGITS)
+               return long_float(self);
+
+       /* If called with two args, defer to float.__round__(). */
+       x = PyLong_AsDouble(self);
+       if (x == -1.0 && PyErr_Occurred())
+               return NULL;
+       self = PyFloat_FromDouble(x);
+       if (self == NULL)
+               return NULL;
+       res = PyObject_CallMethod(self, "__round__", "i", ndigits);
+       Py_DECREF(self);
+       return res;
+#undef UNDEF_NDIGITS
+}
+
 static PyMethodDef long_methods[] = {
+       {"conjugate",   (PyCFunction)long_long, METH_NOARGS,
+        "Returns self, the complex conjugate of any long."},
+       {"__trunc__",   (PyCFunction)long_long, METH_NOARGS,
+         "Truncating an Integral returns itself."},
+       {"__floor__",   (PyCFunction)long_long, METH_NOARGS,
+         "Flooring an Integral returns itself."},
+       {"__ceil__",    (PyCFunction)long_long, METH_NOARGS,
+         "Ceiling of an Integral returns itself."},
+       {"__round__",   (PyCFunction)long_round, METH_VARARGS,
+         "Rounding an Integral returns itself.\n"
+        "Rounding with an ndigits arguments defers to float.__round__."},
        {"__getnewargs__",      (PyCFunction)long_getnewargs,   METH_NOARGS},
        {NULL,          NULL}           /* sentinel */
 };
 
+static PyGetSetDef long_getset[] = {
+    {"real", 
+     (getter)long_long, (setter)NULL,
+     "the real part of a complex number",
+     NULL},
+    {"imag", 
+     (getter)long_getN, (setter)NULL,
+     "the imaginary part of a complex number",
+     (void*)0},
+    {"numerator", 
+     (getter)long_long, (setter)NULL,
+     "the numerator of a rational number in lowest terms",
+     NULL},
+    {"denominator", 
+     (getter)long_getN, (setter)NULL,
+     "the denominator of a rational number in lowest terms",
+     (void*)1},
+    {NULL}  /* Sentinel */
+};
+
 PyDoc_STRVAR(long_doc,
 "long(x[, base]) -> integer\n\
 \n\
@@ -3396,7 +3448,7 @@ static PyNumberMethods long_as_number = {
                        long_divmod,    /*nb_divmod*/
                        long_pow,       /*nb_power*/
        (unaryfunc)     long_neg,       /*nb_negative*/
-       (unaryfunc)     long_pos,       /*tp_positive*/
+       (unaryfunc)     long_long,      /*tp_positive*/
        (unaryfunc)     long_abs,       /*tp_absolute*/
        (inquiry)       long_nonzero,   /*tp_nonzero*/
        (unaryfunc)     long_invert,    /*nb_invert*/
@@ -3461,7 +3513,7 @@ PyTypeObject PyLong_Type = {
        0,                                      /* tp_iternext */
        long_methods,                           /* tp_methods */
        0,                                      /* tp_members */
-       0,                                      /* tp_getset */
+       long_getset,                            /* tp_getset */
        0,                                      /* tp_base */
        0,                                      /* tp_dict */
        0,                                      /* tp_descr_get */
index 69969361c04e8f38f3b5fe1ae1d466f088d5aafb..a7d77de88d81a9177d8c4ea39002f4ee31179064 100644 (file)
@@ -1926,39 +1926,31 @@ For most object types, eval(repr(object)) == object.");
 static PyObject *
 builtin_round(PyObject *self, PyObject *args, PyObject *kwds)
 {
-       double number;
-       double f;
-       int ndigits = 0;
-       int i;
+#define UNDEF_NDIGITS (-0x7fffffff) /* Unlikely ndigits value */
+       int ndigits = UNDEF_NDIGITS;
        static char *kwlist[] = {"number", "ndigits", 0};
+       PyObject *number;
 
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "d|i:round",
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i:round",
                 kwlist, &number, &ndigits))
                 return NULL;
-       f = 1.0;
-       i = abs(ndigits);
-       while  (--i >= 0)
-               f = f*10.0;
-       if (ndigits < 0)
-               number /= f;
-       else
-               number *= f;
-       if (number >= 0.0)
-               number = floor(number + 0.5);
-       else
-               number = ceil(number - 0.5);
-       if (ndigits < 0)
-               number *= f;
-       else
-               number /= f;
-       return PyFloat_FromDouble(number);
+
+        // The py3k branch gets better errors for this by using
+        // _PyType_Lookup(), but since float's mro isn't set in py2.6,
+        // we just use PyObject_CallMethod here.
+        if (ndigits == UNDEF_NDIGITS)
+                return PyObject_CallMethod(number, "__round__", "");
+        else
+                return PyObject_CallMethod(number, "__round__", "i", ndigits);
+#undef UNDEF_NDIGITS
 }
 
 PyDoc_STRVAR(round_doc,
 "round(number[, ndigits]) -> floating point number\n\
 \n\
 Round a number to a given precision in decimal digits (default 0 digits).\n\
-This always returns a floating point number.  Precision may be negative.");
+This returns an int when called with one argument, otherwise a float.\n\
+Precision may be negative.");
 
 static PyObject *
 builtin_sorted(PyObject *self, PyObject *args, PyObject *kwds)
@@ -2039,6 +2031,20 @@ PyDoc_STRVAR(vars_doc,
 Without arguments, equivalent to locals().\n\
 With an argument, equivalent to object.__dict__.");
 
+static PyObject *
+builtin_trunc(PyObject *self, PyObject *number)
+{
+        // XXX: The py3k branch gets better errors for this by using
+        // _PyType_Lookup(), but since float's mro isn't set in py2.6,
+        // we just use PyObject_CallMethod here.
+       return PyObject_CallMethod(number, "__trunc__", "");
+}
+
+PyDoc_STRVAR(trunc_doc,
+"trunc(Real) -> Integral\n\
+\n\
+returns the integral closest to x between 0 and x.");
+
 
 static PyObject*
 builtin_sum(PyObject *self, PyObject *args)
@@ -2387,6 +2393,7 @@ static PyMethodDef builtin_methods[] = {
        {"unichr",      builtin_unichr,     METH_VARARGS, unichr_doc},
 #endif
        {"vars",        builtin_vars,       METH_VARARGS, vars_doc},
+       {"trunc",       builtin_trunc,      METH_O, trunc_doc},
        {"zip",         builtin_zip,        METH_VARARGS, zip_doc},
        {NULL,          NULL},
 };