mirror of
https://github.com/python/cpython.git
synced 2025-09-15 05:06:12 +00:00
Moved Rational._binary_float_to_ratio() to float.as_integer_ratio() because
it's useful outside of rational numbers. This is my first C code that had to do anything significant. Please be more careful when looking over it.
This commit is contained in:
parent
56eadd9d0d
commit
3ea7b41b58
3 changed files with 180 additions and 56 deletions
|
@ -1161,6 +1161,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 = PyInt_FromLong(0);
|
||||
if (numerator == NULL) goto error;
|
||||
denominator = PyInt_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);
|
||||
|
@ -1349,6 +1506,8 @@ 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."},
|
||||
{"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},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue