diff options
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/floatobject.c | 159 | ||||
-rw-r--r-- | Objects/typeobject.c | 18 |
2 files changed, 177 insertions, 0 deletions
diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 859abf0..8ef3261 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -1073,6 +1073,163 @@ float_float(PyObject *v) return v; } +static PyObject * +float_as_integer_ratio(PyObject *v) +{ + double self; + double float_part; + int exponent; + int is_negative; + const int chunk_size = 28; + PyObject *prev; + PyObject *py_chunk = NULL; + PyObject *py_exponent = NULL; + PyObject *numerator = NULL; + PyObject *denominator = NULL; + PyObject *result_pair = NULL; + PyNumberMethods *long_methods; + +#define INPLACE_UPDATE(obj, call) \ + prev = obj; \ + obj = call; \ + Py_DECREF(prev); \ + + CONVERT_TO_DOUBLE(v, self); + + if (Py_IS_INFINITY(self)) { + PyErr_SetString(PyExc_OverflowError, + "Cannot pass infinity to float.as_integer_ratio."); + return NULL; + } +#ifdef Py_NAN + if (Py_IS_NAN(self)) { + PyErr_SetString(PyExc_ValueError, + "Cannot pass nan to float.as_integer_ratio."); + return NULL; + } +#endif + + if (self == 0) { + numerator = PyLong_FromLong(0); + if (numerator == NULL) goto error; + denominator = PyLong_FromLong(1); + if (denominator == NULL) goto error; + result_pair = PyTuple_Pack(2, numerator, denominator); + /* Hand ownership over to the tuple. If the tuple + wasn't created successfully, we want to delete the + ints anyway. */ + Py_DECREF(numerator); + Py_DECREF(denominator); + return result_pair; + } + + /* XXX: Could perhaps handle FLT_RADIX!=2 by using ilogb and + scalbn, but those may not be in C89. */ + PyFPE_START_PROTECT("as_integer_ratio", goto error); + float_part = frexp(self, &exponent); + is_negative = 0; + if (float_part < 0) { + float_part = -float_part; + is_negative = 1; + /* 0.5 <= float_part < 1.0 */ + } + PyFPE_END_PROTECT(float_part); + /* abs(self) == float_part * 2**exponent exactly */ + + /* Suck up chunk_size bits at a time; 28 is enough so that we + suck up all bits in 2 iterations for all known binary + double-precision formats, and small enough to fit in a + long. */ + numerator = PyLong_FromLong(0); + if (numerator == NULL) goto error; + + long_methods = PyLong_Type.tp_as_number; + + py_chunk = PyLong_FromLong(chunk_size); + if (py_chunk == NULL) goto error; + + while (float_part != 0) { + /* invariant: abs(self) == + (numerator + float_part) * 2**exponent exactly */ + long digit; + PyObject *py_digit; + + PyFPE_START_PROTECT("as_integer_ratio", goto error); + /* Pull chunk_size bits out of float_part, into digits. */ + float_part = ldexp(float_part, chunk_size); + digit = (long)float_part; + float_part -= digit; + /* 0 <= float_part < 1 */ + exponent -= chunk_size; + PyFPE_END_PROTECT(float_part); + + /* Shift digits into numerator. */ + // numerator <<= chunk_size + INPLACE_UPDATE(numerator, + long_methods->nb_lshift(numerator, py_chunk)); + if (numerator == NULL) goto error; + + // numerator |= digit + py_digit = PyLong_FromLong(digit); + if (py_digit == NULL) goto error; + INPLACE_UPDATE(numerator, + long_methods->nb_or(numerator, py_digit)); + Py_DECREF(py_digit); + if (numerator == NULL) goto error; + } + + /* Add in the sign bit. */ + if (is_negative) { + INPLACE_UPDATE(numerator, + long_methods->nb_negative(numerator)); + if (numerator == NULL) goto error; + } + + /* now self = numerator * 2**exponent exactly; fold in 2**exponent */ + denominator = PyLong_FromLong(1); + py_exponent = PyLong_FromLong(labs(exponent)); + if (py_exponent == NULL) goto error; + INPLACE_UPDATE(py_exponent, + long_methods->nb_lshift(denominator, py_exponent)); + if (py_exponent == NULL) goto error; + if (exponent > 0) { + INPLACE_UPDATE(numerator, + long_methods->nb_multiply(numerator, + py_exponent)); + if (numerator == NULL) goto error; + } + else { + Py_DECREF(denominator); + denominator = py_exponent; + py_exponent = NULL; + } + + result_pair = PyTuple_Pack(2, numerator, denominator); + +#undef INPLACE_UPDATE +error: + Py_XDECREF(py_exponent); + Py_XDECREF(py_chunk); + Py_XDECREF(denominator); + Py_XDECREF(numerator); + return result_pair; +} + +PyDoc_STRVAR(float_as_integer_ratio_doc, +"float.as_integer_ratio() -> (int, int)\n" +"\n" +"Returns a pair of integers, not necessarily in lowest terms, whose\n" +"ratio is exactly equal to the original float. This method raises an\n" +"OverflowError on infinities and a ValueError on nans. The resulting\n" +"denominator will be positive.\n" +"\n" +">>> (10.0).as_integer_ratio()\n" +"(167772160L, 16777216L)\n" +">>> (0.0).as_integer_ratio()\n" +"(0, 1)\n" +">>> (-.25).as_integer_ratio()\n" +"(-134217728L, 536870912L)"); + static PyObject * float_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds); @@ -1281,6 +1438,8 @@ static PyMethodDef float_methods[] = { {"__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)."}, + {"as_integer_ratio", (PyCFunction)float_as_integer_ratio, METH_NOARGS, + float_as_integer_ratio_doc}, {"__getnewargs__", (PyCFunction)float_getnewargs, METH_NOARGS}, {"__getformat__", (PyCFunction)float_getformat, METH_O|METH_CLASS, float_getformat_doc}, diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 33b2023..1d6336c 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -33,6 +33,24 @@ struct method_cache_entry { static struct method_cache_entry method_cache[1 << MCACHE_SIZE_EXP]; static unsigned int next_version_tag = 0; +static void type_modified(PyTypeObject *); + +unsigned int +PyType_ClearCache(void) +{ + Py_ssize_t i; + unsigned int cur_version_tag = next_version_tag - 1; + + for (i = 0; i < (1 << MCACHE_SIZE_EXP); i++) { + method_cache[i].version = 0; + Py_CLEAR(method_cache[i].name); + method_cache[i].value = NULL; + } + next_version_tag = 0; + /* mark all version tags as invalid */ + type_modified(&PyBaseObject_Type); + return cur_version_tag; +} static void type_modified(PyTypeObject *type) |