Factorial function in Objective-C
Factorial, denoted by n! and defined as the product of all positive integers less than or equal to n.
How to implement such a function in Objective-C? It needs to be fast and, of course, accurate. Needless to say it will have limitations. Factorials very quickly become large. The implementation will use NSUInteger as the numeric type. This type’s size depends on architecture, 32 or 64 bit. Hence its ability to carry factorials will vary. Whatever the case, whatever architectural limitations and capabilities, the implementation will reside within them. No other choice!
Interface
The requirement is purely functional. No class required.
NSUInteger RRFactorial(NSUInteger n);
So very simple. One NSUInteger in, another out. Factorials deal with positive integers only. Hence U for unsigned integer.
Test case
The test case iterates all the integers n between 0 and 19 inclusive and logs the value of n and RRFactorial(n).
int
{
NSAutoreleasePool *pool = ;
NSUInteger n;
for
{
NSLog(@"%lu %lu", (unsigned long)n, (unsigned long)RRFactorial(n));
}
;
return 0;
}
At first, the test fails successfully! It even fails to link because no implementation for the function currently exists.
Implementation
General factorial implementation is extremely simple: given n, just multiply together every integer between 1 and n inclusive. Nothing difficult there.
However, the implementation herein will aim for speed by caching the results dynamically. So having already computed the factorial for any given integer, the answer just becomes a look-up transformation; no calculations required. Space is not at issue. Such a look-up implementation will not usually exceed say 20. After that, answers become silly numbers; e.g. 50! approximately equals 3*pow(10,64). That’s a pretty big number and bigger even than DBL_MAX.
Design-by-contract principles will set boundaries. Give it high values of n and don’t expect sensible answers. That’s a way of saying ‘ignore it!’
NSUInteger
{
// Cache the factorials. Use NSZoneMalloc, Realloc and Free for memory management. For initial revisions, ignore memory leaks at application exit.
static NSUInteger *factorials = NULL;
static NSUInteger numberOfFactorials;
if
{
factorials = NSZoneMalloc(NSDefaultMallocZone(), sizeof(NSUInteger));
factorials = 1;
numberOfFactorials = 1;
}
if
{
NSUInteger *newFactorials = NSZoneRealloc(NSDefaultMallocZone(), factorials, (n + 1) * sizeof(NSUInteger));
factorials = newFactorials;
while
{
factorials = numberOfFactorials * factorials;
numberOfFactorials++;
}
}
return factorials;
}
Testing
Executing the test case produces:
0 1 1 1 2 2 3 6 4 24 5 120 6 720 7 5040 8 40320 9 362880 10 3628800 11 39916800 12 479001600 13 1932053504 14 1278945280 15 2004310016 16 2004189184 17 4006445056 18 3396534272 19 109641728
Compare this to the factorial tableau at Wikipedia. It’s a match!
Or is it? Note deviations starting at n=13. That’s because 13! exceeds unsigned integer maximum of about 4,000 million on 32-bit platforms.
64-bit testing
Switching to pure 64-bit, by making architectures (ARCHS) equal to ppc64 plus x86_64, and rerunning the test produces a different result.
0 1 1 1 2 2 3 6 4 24 5 120 6 720 7 5040 8 40320 9 362880 10 3628800 11 39916800 12 479001600 13 6227020800 14 87178291200 15 1307674368000 16 20922789888000 17 355687428096000 18 6402373705728000 19 121645100408832000
Much better. Or should I say less limited. Better is just a point of view in this case because even the 64-bit version will eventually find its limitations. Man’s got to know his limitations! (“Dirty” Harry Callahan, Magnum Force, 1973)
Failing gracefully
At present, nothing gracefully happens when n and n! reach limits of the host machine. That’s not very satisfying. Should it not stop after overflow? Nor is the fact that the function remains insensitive to memory allocation failures a source of satisfaction. Should it throw exceptions? That’s messy.
There’s another approach.
Since the factorial cache has limited size under normal conditions, why not make the cache static; its size depending on the compiler’s NSUInteger width. Once the cache is full, answer NSUIntegerMax indicating factorial out of integer range. That would solve two problems at the same time; two birds, one stone.
Compiler manifest constant __LP64__ signals 64-bit architecture. However, there is also a special NS_BUILD_32_LIKE_64 macro for making NSUInteger 64 bits wide regardless.
Updated version below. It eliminates dependence on memory allocation and obviates exception handling for allocation failure and boundary overflow. All in one fell swoop. I’ve also obfuscated variable names to make it more mathematical in nature: i for the index of the last computed factorial, f for the factorials, N for the upper inclusive boundary of n. Is that bad?

Listings
RRFactorial.h
NSUInteger RRFactorial(NSUInteger n);
// Answers the factorial of n, or NSUIntegerMax if factorial exceeds
// NSUInteger type's upper boundary. Uses Foundation framework's NSUInteger
// type to carry factorials.
RRFactorial.m
NSUInteger
{
// Use preprocessor macros to determine the maximum value of n computable
// within the limits prescribed by NSUInteger, whose bit-width depends on
// architecture: either 64 or 32 bits.
// 20! = 2,432,902,008,176,640,000
// 12! = 479,001,600
if (n > N) return NSUIntegerMax;
static NSUInteger f;
static NSUInteger i = 0;
if
{
f = 1;
i = 1;
}
while
{
f = i * f;
i++;
}
return f;
}
Download sources
You can download the MIT-licensed sources here.
over 1 year later
Hi guys,
I know this might be a bit off topic but seeing that a bunch of you own websites, where would the best place be to host. Someone recommended I use Blue Host<//url> for $6.95 a month which seems like a great deal. Anyone here on blog.pioneeringsoftware.co.uk using them?
over 1 year later
Hi Broommaarresk,
Thanks for your comment. No problem going off topic. We’re not ‘precious’ about things like that. It’s just normal!
We have no experience with Blue Host. However, we can recommend Heroku. We’re currently in the process of migrating all our sites to Rails cloud via Heroku.
Kind regards,
Roy