Issue #2138: Add math.factorial().

This commit is contained in:
Raymond Hettinger 2008-06-09 06:54:45 +00:00
parent dd96db63f6
commit ecbdd2e9b0
4 changed files with 71 additions and 0 deletions

View File

@ -42,6 +42,10 @@ Number-theoretic and representation functions:
Return the absolute value of *x*. Return the absolute value of *x*.
.. function:: factorial(x)
Return *x* factorial. Raises :exc:`ValueError` if *x* is not intergral or
is negative.
.. function:: floor(x) .. function:: floor(x)

View File

@ -6,6 +6,7 @@ import unittest
import math import math
import os import os
import sys import sys
import random
eps = 1E-05 eps = 1E-05
NAN = float('nan') NAN = float('nan')
@ -277,6 +278,20 @@ class MathTests(unittest.TestCase):
self.ftest('fabs(0)', math.fabs(0), 0) self.ftest('fabs(0)', math.fabs(0), 0)
self.ftest('fabs(1)', math.fabs(1), 1) self.ftest('fabs(1)', math.fabs(1), 1)
def testFactorial(self):
def fact(n):
result = 1
for i in range(1, int(n)+1):
result *= i
return result
values = range(10) + [50, 100, 500]
random.shuffle(values)
for x in range(10):
for cast in (int, long, float):
self.assertEqual(math.factorial(cast(x)), fact(x), (x, fact(x), math.factorial(x)))
self.assertRaises(ValueError, math.factorial, -1)
self.assertRaises(ValueError, math.factorial, math.pi)
def testFloor(self): def testFloor(self):
self.assertRaises(TypeError, math.floor) self.assertRaises(TypeError, math.floor)
# These types will be int in py3k. # These types will be int in py3k.

View File

@ -40,6 +40,8 @@ Core and Builtins
Extension Modules Extension Modules
----------------- -----------------
- Issue #2138: Add factorial() the math module.
- The heapq module does comparisons using LT instead of LE. This - The heapq module does comparisons using LT instead of LE. This
makes its implementation match that used by list.sort(). makes its implementation match that used by list.sort().

View File

@ -512,6 +512,55 @@ PyDoc_STRVAR(math_sum_doc,
Return an accurate floating point sum of values in the iterable.\n\ Return an accurate floating point sum of values in the iterable.\n\
Assumes IEEE-754 floating point arithmetic."); Assumes IEEE-754 floating point arithmetic.");
static PyObject *
math_factorial(PyObject *self, PyObject *arg)
{
long i, x;
PyObject *result, *iobj, *newresult;
if (PyFloat_Check(arg)) {
double dx = PyFloat_AS_DOUBLE((PyFloatObject *)arg);
if (dx != floor(dx)) {
PyErr_SetString(PyExc_ValueError,
"factorial() only accepts integral values");
return NULL;
}
}
x = PyInt_AsLong(arg);
if (x == -1 && PyErr_Occurred())
return NULL;
if (x < 0) {
PyErr_SetString(PyExc_ValueError,
"factorial() not defined for negative values");
return NULL;
}
result = (PyObject *)PyInt_FromLong(1);
if (result == NULL)
return NULL;
for (i=1 ; i<=x ; i++) {
iobj = (PyObject *)PyInt_FromLong(i);
if (iobj == NULL)
goto error;
newresult = PyNumber_Multiply(result, iobj);
Py_DECREF(iobj);
if (newresult == NULL)
goto error;
Py_DECREF(result);
result = newresult;
}
return result;
error:
Py_DECREF(result);
Py_XDECREF(iobj);
return NULL;
}
PyDoc_STRVAR(math_factorial_doc, "Return n!");
static PyObject * static PyObject *
math_trunc(PyObject *self, PyObject *number) math_trunc(PyObject *self, PyObject *number)
{ {
@ -949,6 +998,7 @@ static PyMethodDef math_methods[] = {
{"degrees", math_degrees, METH_O, math_degrees_doc}, {"degrees", math_degrees, METH_O, math_degrees_doc},
{"exp", math_exp, METH_O, math_exp_doc}, {"exp", math_exp, METH_O, math_exp_doc},
{"fabs", math_fabs, METH_O, math_fabs_doc}, {"fabs", math_fabs, METH_O, math_fabs_doc},
{"factorial", math_factorial, METH_O, math_factorial_doc},
{"floor", math_floor, METH_O, math_floor_doc}, {"floor", math_floor, METH_O, math_floor_doc},
{"fmod", math_fmod, METH_VARARGS, math_fmod_doc}, {"fmod", math_fmod, METH_VARARGS, math_fmod_doc},
{"frexp", math_frexp, METH_O, math_frexp_doc}, {"frexp", math_frexp, METH_O, math_frexp_doc},