mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +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
	
	 Jeffrey Yasskin
						Jeffrey Yasskin