Continue rolling back pep-3141 changes that changed behavior from 2.5. This

round included:
 * Revert round to its 2.6 behavior (half away from 0).
 * Because round, floor, and ceil always return float again, it's no
   longer necessary to have them delegate to __xxx___, so I've ripped
   that out of their implementations and the Real ABC. This also helps
   in implementing types that work in both 2.6 and 3.0: you return int
   from the __xxx__ methods, and let it get enabled by the version
   upgrade.
 * Make pow(-1, .5) raise a ValueError again.
This commit is contained in:
Jeffrey Yasskin 2008-01-05 08:47:13 +00:00
parent f7476c4d46
commit 9871d8fe22
13 changed files with 75 additions and 252 deletions

View File

@ -986,13 +986,10 @@ available. They are listed here in alphabetical order.
.. function:: round(x[, n]) .. function:: round(x[, n])
Return the floating point value *x* rounded to *n* digits after the decimal Return the floating point value *x* rounded to *n* digits after the decimal
point. If *n* is omitted, it defaults to zero. Values are rounded to the point. If *n* is omitted, it defaults to zero. The result is a floating point
closest multiple of 10 to the power minus *n*; if two multiples are equally number. Values are rounded to the closest multiple of 10 to the power minus
close, rounding is done toward the even choice (so, for example, both *n*; if two multiples are equally close, rounding is done away from 0 (so. for
``round(0.5)`` and ``round(-0.5)`` are ``0``, and ``round(1.5)`` is example, ``round(0.5)`` is ``1.0`` and ``round(-0.5)`` is ``-1.0``).
``2``). Delegates to ``x.__round__(n)``.
.. versionchanged:: 2.6
.. function:: set([iterable]) .. function:: set([iterable])

View File

@ -26,9 +26,8 @@ Number-theoretic and representation functions:
.. function:: ceil(x) .. function:: ceil(x)
Return the ceiling of *x* as a float, the smallest integer value greater than Return the ceiling of *x* as a float, the smallest integer value greater than or
or equal to *x*. If *x* is not a float, delegates to ``x.__ceil__()``, which equal to *x*.
should return an :class:`Integral` value.
.. function:: copysign(x, y) .. function:: copysign(x, y)
@ -46,9 +45,8 @@ Number-theoretic and representation functions:
.. function:: floor(x) .. function:: floor(x)
Return the floor of *x* as a float, the largest integer value less than or Return the floor of *x* as a float, the largest integer value less than or equal
equal to *x*. If *x* is not a float, delegates to ``x.__floor__()``, which to *x*.
should return an :class:`Integral` value.
.. function:: fmod(x, y) .. function:: fmod(x, y)

View File

@ -341,11 +341,11 @@ Notes:
pair: C; language pair: C; language
Conversion from floating point to (long or plain) integer may round or Conversion from floating point to (long or plain) integer may round or
truncate as in C. truncate as in C; see functions :func:`math.floor` and :func:`math.ceil` for
well-defined conversions.
.. deprecated:: 2.6 .. deprecated:: 2.6
Instead, convert floats to long explicitly with :func:`trunc`, Instead, convert floats to long explicitly with :func:`trunc`.
:func:`math.floor`, or :func:`math.ceil`.
(3) (3)
See :ref:`built-in-funcs` for a full description. See :ref:`built-in-funcs` for a full description.
@ -369,19 +369,19 @@ Notes:
All :class:`numbers.Real` types (:class:`int`, :class:`long`, and All :class:`numbers.Real` types (:class:`int`, :class:`long`, and
:class:`float`) also include the following operations: :class:`float`) also include the following operations:
+--------------------+--------------------------------+--------+ +--------------------+------------------------------------+--------+
| Operation | Result | Notes | | Operation | Result | Notes |
+====================+================================+========+ +====================+====================================+========+
| ``trunc(x)`` | *x* truncated to Integral | | | ``trunc(x)`` | *x* truncated to Integral | |
+--------------------+--------------------------------+--------+ +--------------------+------------------------------------+--------+
| ``round(x[, n])`` | *x* rounded to n digits, | | | ``round(x[, n])`` | *x* rounded to n digits, | |
| | rounding half to even. If n is | | | | rounding half to even. If n is | |
| | omitted, it defaults to 0. | | | | omitted, it defaults to 0. | |
+--------------------+--------------------------------+--------+ +--------------------+------------------------------------+--------+
| ``math.floor(x)`` | the greatest Integral <= *x* | | | ``math.floor(x)`` | the greatest integral float <= *x* | |
+--------------------+--------------------------------+--------+ +--------------------+------------------------------------+--------+
| ``math.ceil(x)`` | the least Integral >= *x* | | | ``math.ceil(x)`` | the least integral float >= *x* | |
+--------------------+--------------------------------+--------+ +--------------------+------------------------------------+--------+
.. XXXJH exceptions: overflow (when? what operations?) zerodivision .. XXXJH exceptions: overflow (when? what operations?) zerodivision

View File

@ -801,8 +801,7 @@ were of integer types and the second argument was negative, an exception was
raised). raised).
Raising ``0.0`` to a negative power results in a :exc:`ZeroDivisionError`. Raising ``0.0`` to a negative power results in a :exc:`ZeroDivisionError`.
Raising a negative number to a fractional power results in a :class:`complex` Raising a negative number to a fractional power results in a :exc:`ValueError`.
number. (Since Python 2.6. In earlier versions it raised a :exc:`ValueError`.)
.. _unary: .. _unary:

View File

@ -189,25 +189,6 @@ class Real(Complex):
""" """
raise NotImplementedError 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): def __divmod__(self, other):
"""divmod(self, other): The pair (self // other, self % other). """divmod(self, other): The pair (self // other, self % other).

View File

@ -1456,13 +1456,12 @@ class BuiltinTest(unittest.TestCase):
else: else:
self.assertAlmostEqual(pow(x, y, z), 24.0) 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(TypeError, pow, -1, -2, 3)
self.assertRaises(ValueError, pow, 1, 2, 0) self.assertRaises(ValueError, pow, 1, 2, 0)
self.assertRaises(TypeError, pow, -1L, -2L, 3L) self.assertRaises(TypeError, pow, -1L, -2L, 3L)
self.assertRaises(ValueError, pow, 1L, 2L, 0L) self.assertRaises(ValueError, pow, 1L, 2L, 0L)
# Will return complex in 3.0:
self.assertRaises(ValueError, pow, -342.43, 0.234)
self.assertRaises(TypeError, pow) self.assertRaises(TypeError, pow)
@ -1664,11 +1663,11 @@ class BuiltinTest(unittest.TestCase):
self.assertEqual(type(round(-8.0, 0)), float) self.assertEqual(type(round(-8.0, 0)), float)
self.assertEqual(type(round(-8.0, 1)), float) self.assertEqual(type(round(-8.0, 1)), float)
# Check even / odd rounding behaviour # Check half rounding behaviour.
self.assertEqual(round(5.5), 6) self.assertEqual(round(5.5), 6)
self.assertEqual(round(6.5), 6) self.assertEqual(round(6.5), 7)
self.assertEqual(round(-5.5), -6) self.assertEqual(round(-5.5), -6)
self.assertEqual(round(-6.5), -6) self.assertEqual(round(-6.5), -7)
# Check behavior on ints # Check behavior on ints
self.assertEqual(round(0), 0) self.assertEqual(round(0), 0)
@ -1686,8 +1685,8 @@ class BuiltinTest(unittest.TestCase):
# test generic rounding delegation for reals # test generic rounding delegation for reals
class TestRound(object): class TestRound(object):
def __round__(self): def __float__(self):
return 23 return 23.0
class TestNoRound(object): class TestNoRound(object):
pass pass
@ -1695,13 +1694,12 @@ class BuiltinTest(unittest.TestCase):
self.assertEqual(round(TestRound()), 23) self.assertEqual(round(TestRound()), 23)
self.assertRaises(TypeError, round, 1, 2, 3) self.assertRaises(TypeError, round, 1, 2, 3)
# XXX: This is not ideal, but see the comment in builtin_round(). self.assertRaises(TypeError, round, TestNoRound())
self.assertRaises(AttributeError, round, TestNoRound())
t = TestNoRound() t = TestNoRound()
t.__round__ = lambda *args: args t.__float__ = lambda *args: args
self.assertEquals((), round(t)) self.assertRaises(TypeError, round, t)
self.assertEquals((0,), round(t, 0)) self.assertRaises(TypeError, round, t, 0)
def test_setattr(self): def test_setattr(self):
setattr(sys, 'spam', 1) setattr(sys, 'spam', 1)

View File

@ -385,9 +385,7 @@ class LongTest(unittest.TestCase):
"1. ** huge", "huge ** 1.", "1. ** mhuge", "mhuge ** 1.", "1. ** huge", "huge ** 1.", "1. ** mhuge", "mhuge ** 1.",
"math.sin(huge)", "math.sin(mhuge)", "math.sin(huge)", "math.sin(mhuge)",
"math.sqrt(huge)", "math.sqrt(mhuge)", # should do better "math.sqrt(huge)", "math.sqrt(mhuge)", # should do better
# math.floor() of an int returns an int now "math.floor(huge)", "math.floor(mhuge)"]:
##"math.floor(huge)", "math.floor(mhuge)",
]:
self.assertRaises(OverflowError, eval, test, namespace) self.assertRaises(OverflowError, eval, test, namespace)

View File

@ -63,8 +63,8 @@ class MathTests(unittest.TestCase):
self.ftest('ceil(-1.5)', math.ceil(-1.5), -1) self.ftest('ceil(-1.5)', math.ceil(-1.5), -1)
class TestCeil(object): class TestCeil(object):
def __ceil__(self): def __float__(self):
return 42 return 41.3
class TestNoCeil(object): class TestNoCeil(object):
pass pass
self.ftest('ceil(TestCeil())', math.ceil(TestCeil()), 42) self.ftest('ceil(TestCeil())', math.ceil(TestCeil()), 42)
@ -123,8 +123,8 @@ 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): class TestFloor(object):
def __floor__(self): def __float__(self):
return 42 return 42.3
class TestNoFloor(object): class TestNoFloor(object):
pass pass
self.ftest('floor(TestFloor())', math.floor(TestFloor()), 42) self.ftest('floor(TestFloor())', math.floor(TestFloor()), 42)

View File

@ -107,28 +107,9 @@ FUNC1(atan, atan,
FUNC2(atan2, atan2, FUNC2(atan2, atan2,
"atan2(y, x)\n\nReturn the arc tangent (measured in radians) of y/x.\n" "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.") "Unlike atan(y/x), the signs of both x and y are considered.")
FUNC1(ceil, ceil,
static PyObject * math_ceil(PyObject *self, PyObject *number) { "ceil(x)\n\nReturn the ceiling of x as a float.\n"
static PyObject *ceil_str = NULL; "This is the smallest integral value >= x.")
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, FUNC1(cos, cos,
"cos(x)\n\nReturn the cosine of x (measured in radians).") "cos(x)\n\nReturn the cosine of x (measured in radians).")
FUNC1(cosh, cosh, FUNC1(cosh, cosh,
@ -147,28 +128,9 @@ FUNC1(exp, exp,
"exp(x)\n\nReturn e raised to the power of x.") "exp(x)\n\nReturn e raised to the power of x.")
FUNC1(fabs, fabs, FUNC1(fabs, fabs,
"fabs(x)\n\nReturn the absolute value of the float x.") "fabs(x)\n\nReturn the absolute value of the float x.")
FUNC1(floor, floor,
static PyObject * math_floor(PyObject *self, PyObject *number) { "floor(x)\n\nReturn the floor of x as a float.\n"
static PyObject *floor_str = NULL; "This is the largest integral value <= x.")
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, FUNC2(fmod, fmod,
"fmod(x,y)\n\nReturn fmod(x, y), according to platform C." "fmod(x,y)\n\nReturn fmod(x, y), according to platform C."
" x % y may differ.") " x % y may differ.")

View File

@ -986,10 +986,9 @@ float_pow(PyObject *v, PyObject *w, PyObject *z)
* bugs so we have to figure it out ourselves. * bugs so we have to figure it out ourselves.
*/ */
if (iw != floor(iw)) { if (iw != floor(iw)) {
/* Negative numbers raised to fractional powers PyErr_SetString(PyExc_ValueError, "negative number "
* become complex. "cannot be raised to a fractional power");
*/ return NULL;
return PyComplex_Type.tp_as_number->nb_power(v, w, z);
} }
/* iw is an exact integer, albeit perhaps a very large one. /* iw is an exact integer, albeit perhaps a very large one.
* -1 raised to an exact integer should never be exceptional. * -1 raised to an exact integer should never be exceptional.
@ -1098,54 +1097,6 @@ float_trunc(PyObject *v)
return PyLong_FromDouble(wholepart); 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 * static PyObject *
float_float(PyObject *v) float_float(PyObject *v)
{ {
@ -1344,9 +1295,6 @@ static PyMethodDef float_methods[] = {
"Returns self, the complex conjugate of any float."}, "Returns self, the complex conjugate of any float."},
{"__trunc__", (PyCFunction)float_trunc, METH_NOARGS, {"__trunc__", (PyCFunction)float_trunc, METH_NOARGS,
"Returns the Integral closest to x between 0 and x."}, "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}, {"__getnewargs__", (PyCFunction)float_getnewargs, METH_NOARGS},
{"__getformat__", (PyCFunction)float_getformat, {"__getformat__", (PyCFunction)float_getformat,
METH_O|METH_CLASS, float_getformat_doc}, METH_O|METH_CLASS, float_getformat_doc},

View File

@ -1056,43 +1056,11 @@ int_getN(PyIntObject *v, void *context) {
return PyInt_FromLong((intptr_t)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[] = { static PyMethodDef int_methods[] = {
{"conjugate", (PyCFunction)int_int, METH_NOARGS, {"conjugate", (PyCFunction)int_int, METH_NOARGS,
"Returns self, the complex conjugate of any int."}, "Returns self, the complex conjugate of any int."},
{"__trunc__", (PyCFunction)int_int, METH_NOARGS, {"__trunc__", (PyCFunction)int_int, METH_NOARGS,
"Truncating an Integral returns itself."}, "Truncating an Integral returns itself."},
{"__floor__", (PyCFunction)int_float, METH_NOARGS,
"Flooring an Integral returns itself."},
{"__ceil__", (PyCFunction)int_float, 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}, {"__getnewargs__", (PyCFunction)int_getnewargs, METH_NOARGS},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };

View File

@ -3370,45 +3370,11 @@ long_getN(PyLongObject *v, void *context) {
return PyLong_FromLong((intptr_t)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[] = { static PyMethodDef long_methods[] = {
{"conjugate", (PyCFunction)long_long, METH_NOARGS, {"conjugate", (PyCFunction)long_long, METH_NOARGS,
"Returns self, the complex conjugate of any long."}, "Returns self, the complex conjugate of any long."},
{"__trunc__", (PyCFunction)long_long, METH_NOARGS, {"__trunc__", (PyCFunction)long_long, METH_NOARGS,
"Truncating an Integral returns itself."}, "Truncating an Integral returns itself."},
{"__floor__", (PyCFunction)long_float, METH_NOARGS,
"Flooring an Integral returns itself."},
{"__ceil__", (PyCFunction)long_float, 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}, {"__getnewargs__", (PyCFunction)long_getnewargs, METH_NOARGS},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };

View File

@ -1926,31 +1926,39 @@ For most object types, eval(repr(object)) == object.");
static PyObject * static PyObject *
builtin_round(PyObject *self, PyObject *args, PyObject *kwds) builtin_round(PyObject *self, PyObject *args, PyObject *kwds)
{ {
#define UNDEF_NDIGITS (-0x7fffffff) /* Unlikely ndigits value */ double number;
int ndigits = UNDEF_NDIGITS; double f;
int ndigits = 0;
int i;
static char *kwlist[] = {"number", "ndigits", 0}; static char *kwlist[] = {"number", "ndigits", 0};
PyObject *number;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i:round", if (!PyArg_ParseTupleAndKeywords(args, kwds, "d|i:round",
kwlist, &number, &ndigits)) kwlist, &number, &ndigits))
return NULL; return NULL;
f = 1.0;
// The py3k branch gets better errors for this by using i = abs(ndigits);
// _PyType_Lookup(), but since float's mro isn't set in py2.6, while (--i >= 0)
// we just use PyObject_CallMethod here. f = f*10.0;
if (ndigits == UNDEF_NDIGITS) if (ndigits < 0)
return PyObject_CallMethod(number, "__round__", ""); number /= f;
else else
return PyObject_CallMethod(number, "__round__", "i", ndigits); number *= f;
#undef UNDEF_NDIGITS 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);
} }
PyDoc_STRVAR(round_doc, PyDoc_STRVAR(round_doc,
"round(number[, ndigits]) -> floating point number\n\ "round(number[, ndigits]) -> floating point number\n\
\n\ \n\
Round a number to a given precision in decimal digits (default 0 digits).\n\ Round a number to a given precision in decimal digits (default 0 digits).\n\
This returns an int when called with one argument, otherwise a float.\n\ This always returns a floating point number. Precision may be negative.");
Precision may be negative.");
static PyObject * static PyObject *
builtin_sorted(PyObject *self, PyObject *args, PyObject *kwds) builtin_sorted(PyObject *self, PyObject *args, PyObject *kwds)