]> granicus.if.org Git - python/commitdiff
Issue #2138: Add math.factorial().
authorRaymond Hettinger <python@rcn.com>
Mon, 9 Jun 2008 06:54:45 +0000 (06:54 +0000)
committerRaymond Hettinger <python@rcn.com>
Mon, 9 Jun 2008 06:54:45 +0000 (06:54 +0000)
Doc/library/math.rst
Lib/test/test_math.py
Misc/NEWS
Modules/mathmodule.c

index b98f1649a32af12f2e919407362bfd2188b2349f..d6e0205398a98ae8b04bc0549745ad0b8defe18e 100644 (file)
@@ -42,6 +42,10 @@ Number-theoretic and representation functions:
 
    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)
 
index 426ca7b6d5956616a5e177304f9ccdf33ef82d59..3123954c922f6c889be2efae6548c8a020039c47 100644 (file)
@@ -6,6 +6,7 @@ import unittest
 import math
 import os
 import sys
+import random
 
 eps = 1E-05
 NAN = float('nan')
@@ -277,6 +278,20 @@ class MathTests(unittest.TestCase):
         self.ftest('fabs(0)', math.fabs(0), 0)
         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):
         self.assertRaises(TypeError, math.floor)
         # These types will be int in py3k.
index b5afbccc5919f5843260ecf7cd8613db735e385e..4337e5daff9f32d66141d7cf28234b0fa0ec5d0a 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -40,6 +40,8 @@ Core and Builtins
 Extension Modules
 -----------------
 
+- Issue #2138: Add factorial() the math module.
+
 - The heapq module does comparisons using LT instead of LE.  This
   makes its implementation match that used by list.sort().
 
index 5520ca963791d015b57a4d7431addca5fc7e2802..7ace9972c387bf9ea09c2c84cee54b58ae9e6aa9 100644 (file)
@@ -512,6 +512,55 @@ PyDoc_STRVAR(math_sum_doc,
 Return an accurate floating point sum of values in the iterable.\n\
 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 *
 math_trunc(PyObject *self, PyObject *number)
 {
@@ -949,6 +998,7 @@ static PyMethodDef math_methods[] = {
        {"degrees",     math_degrees,   METH_O,         math_degrees_doc},
        {"exp",         math_exp,       METH_O,         math_exp_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},
        {"fmod",        math_fmod,      METH_VARARGS,   math_fmod_doc},
        {"frexp",       math_frexp,     METH_O,         math_frexp_doc},