From 9871d8fe22566acf68bf336d04d3a1dbd51f3269 Mon Sep 17 00:00:00 2001 From: Jeffrey Yasskin Date: Sat, 5 Jan 2008 08:47:13 +0000 Subject: [PATCH] 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. --- Doc/library/functions.rst | 11 +++---- Doc/library/math.rst | 10 +++--- Doc/library/stdtypes.rst | 32 +++++++++---------- Doc/reference/expressions.rst | 3 +- Lib/numbers.py | 19 ------------ Lib/test/test_builtin.py | 24 +++++++-------- Lib/test/test_long.py | 4 +-- Lib/test/test_math.py | 8 ++--- Modules/mathmodule.c | 50 ++++-------------------------- Objects/floatobject.c | 58 ++--------------------------------- Objects/intobject.c | 32 ------------------- Objects/longobject.c | 34 -------------------- Python/bltinmodule.c | 42 +++++++++++++++---------- 13 files changed, 75 insertions(+), 252 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 3236ccdc512..0133e5cf6fd 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -986,13 +986,10 @@ 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. 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 + 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``). .. function:: set([iterable]) diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 8c9f0f84ce1..6c781041a18 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -26,9 +26,8 @@ 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*. If *x* is not a float, delegates to ``x.__ceil__()``, which - should return an :class:`Integral` value. + Return the ceiling of *x* as a float, the smallest integer value greater than or + equal to *x*. .. function:: copysign(x, y) @@ -46,9 +45,8 @@ 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*. If *x* is not a float, delegates to ``x.__floor__()``, which - should return an :class:`Integral` value. + Return the floor of *x* as a float, the largest integer value less than or equal + to *x*. .. function:: fmod(x, y) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 7352a1d74a8..7a0e24dcc91 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -341,11 +341,11 @@ Notes: pair: C; language 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 - Instead, convert floats to long explicitly with :func:`trunc`, - :func:`math.floor`, or :func:`math.ceil`. + Instead, convert floats to long explicitly with :func:`trunc`. (3) See :ref:`built-in-funcs` for a full description. @@ -369,19 +369,19 @@ Notes: 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* | | -+--------------------+--------------------------------+--------+ ++--------------------+------------------------------------+--------+ +| 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 float <= *x* | | ++--------------------+------------------------------------+--------+ +| ``math.ceil(x)`` | the least integral float >= *x* | | ++--------------------+------------------------------------+--------+ .. XXXJH exceptions: overflow (when? what operations?) zerodivision diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 9c416f85963..ea2bb1a057d 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -801,8 +801,7 @@ 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 :class:`complex` -number. (Since Python 2.6. In earlier versions it raised a :exc:`ValueError`.) +Raising a negative number to a fractional power results in a :exc:`ValueError`. .. _unary: diff --git a/Lib/numbers.py b/Lib/numbers.py index d23fa34eddc..3c13290b133 100644 --- a/Lib/numbers.py +++ b/Lib/numbers.py @@ -189,25 +189,6 @@ class Real(Complex): """ 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). diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 255fb969611..d56e6fff87d 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1456,13 +1456,12 @@ 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) + # Will return complex in 3.0: + self.assertRaises(ValueError, pow, -342.43, 0.234) 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, 1)), float) - # Check even / odd rounding behaviour + # Check half rounding behaviour. 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(-6.5), -6) + self.assertEqual(round(-6.5), -7) # Check behavior on ints self.assertEqual(round(0), 0) @@ -1686,8 +1685,8 @@ class BuiltinTest(unittest.TestCase): # test generic rounding delegation for reals class TestRound(object): - def __round__(self): - return 23 + def __float__(self): + return 23.0 class TestNoRound(object): pass @@ -1695,13 +1694,12 @@ class BuiltinTest(unittest.TestCase): 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()) + self.assertRaises(TypeError, round, TestNoRound()) t = TestNoRound() - t.__round__ = lambda *args: args - self.assertEquals((), round(t)) - self.assertEquals((0,), round(t, 0)) + t.__float__ = lambda *args: args + self.assertRaises(TypeError, round, t) + self.assertRaises(TypeError, round, t, 0) def test_setattr(self): setattr(sys, 'spam', 1) diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index d74535040a4..1709de5b0d9 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -385,9 +385,7 @@ 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() 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) diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 5313c3cad03..16f0f4df760 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -63,8 +63,8 @@ class MathTests(unittest.TestCase): self.ftest('ceil(-1.5)', math.ceil(-1.5), -1) class TestCeil(object): - def __ceil__(self): - return 42 + def __float__(self): + return 41.3 class TestNoCeil(object): pass 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) class TestFloor(object): - def __floor__(self): - return 42 + def __float__(self): + return 42.3 class TestNoFloor(object): pass self.ftest('floor(TestFloor())', math.floor(TestFloor()), 42) diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 44a9d93b2ae..5e52e785416 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -107,28 +107,9 @@ 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.") - -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(ceil, ceil, + "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, @@ -147,28 +128,9 @@ 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.") - -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."); - +FUNC1(floor, floor, + "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.") diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 574b96a4e74..ff23d331619 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -986,10 +986,9 @@ float_pow(PyObject *v, PyObject *w, PyObject *z) * bugs so we have to figure it out ourselves. */ if (iw != floor(iw)) { - /* Negative numbers raised to fractional powers - * become complex. - */ - return PyComplex_Type.tp_as_number->nb_power(v, w, z); + PyErr_SetString(PyExc_ValueError, "negative number " + "cannot be raised to a fractional power"); + return NULL; } /* iw is an exact integer, albeit perhaps a very large one. * -1 raised to an exact integer should never be exceptional. @@ -1098,54 +1097,6 @@ float_trunc(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) { @@ -1344,9 +1295,6 @@ static PyMethodDef float_methods[] = { "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}, diff --git a/Objects/intobject.c b/Objects/intobject.c index 96d7f76ab1c..edb8e4f55ab 100644 --- a/Objects/intobject.c +++ b/Objects/intobject.c @@ -1056,43 +1056,11 @@ 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_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}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/longobject.c b/Objects/longobject.c index eea5c3bad50..1094670303e 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3370,45 +3370,11 @@ 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_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}, {NULL, NULL} /* sentinel */ }; diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index a7d77de88d8..84a00082609 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1926,31 +1926,39 @@ For most object types, eval(repr(object)) == object."); static PyObject * builtin_round(PyObject *self, PyObject *args, PyObject *kwds) { -#define UNDEF_NDIGITS (-0x7fffffff) /* Unlikely ndigits value */ - int ndigits = UNDEF_NDIGITS; + double number; + double f; + int ndigits = 0; + int i; static char *kwlist[] = {"number", "ndigits", 0}; - PyObject *number; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i:round", - kwlist, &number, &ndigits)) - return NULL; - - // 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 + if (!PyArg_ParseTupleAndKeywords(args, kwds, "d|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); } 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 returns an int when called with one argument, otherwise a float.\n\ -Precision may be negative."); +This always returns a floating point number. Precision may be negative."); static PyObject * builtin_sorted(PyObject *self, PyObject *args, PyObject *kwds)