Clinton Montague

Developer, learner of things, functional programming enthusiast, hacker, and all round inquisitor.

Number to words function in PHP

November 16, 2008

Please note!! I have shamelessly stolen this article from my old and (even if I do say so myself) rather boring blog. So please ignore the terrible writing style! I’ll rewrite this article in the future, but until then, I present you with version 1.0

In a recent project at work, I had the task of taking a number and converting it to a written word format for legal clarity. Initially, it sounds like quite a complex task, how would I go about turning ‘154,307’ into ‘One hundred and fifty four thousand, three hundred and seven’? A side note to this post is that it uses ‘English’ rather than ‘American’ translations, i.e. it will convert 107 to ‘One hundred and seven’, not ‘One hundred seven’.

So what do we know about written numbers? We know that the numbers zero to 19 are ‘irregular’ and from 20 onwards, they follow a pattern (i.e. 11 isn’t ten-one, it’s eleven, but 21, 31, 41 all the pattern of having ‘one’ in the name). We also know that any 3 digit number starts with something Hundred, and any number with between 4 and 6 digits is something Thousand. But luckily, this something is a number between 1 and 999, which we already have information about. The function I have written only supports numbers up to 999,999 but it is easy to extend.

The most logical way to store the names of the numbers is in an array

$words = array ('zero',
		'one',
		'two',
		'three',
		'four',
		'five',
		'six',
		'seven',
		'eight',
		'nine',
		'ten',
		'eleven',
		'twelve',
		'thirteen',
		'fourteen',
		'fifteen',
		'sixteen',
		'seventeen',
		'eighteen',
		'nineteen',
		'twenty',
		30=> 'thirty',
		40 => 'fourty',
		50 => 'fifty',
		60 => 'sixty',
		70 => 'seventy',
		80 => 'eighty',
		90 => 'ninety',
		100 => 'hundred',
		1000=> 'thousand');

It is not necessary to have indexes for the first 20 numbers, as the automatic indexes are the correct numbers. It’s only when we get to 30 that we need to give the words indexes, if I simply carried on, then 30 would be at index 21 which is not the most sensible idea for reasons which will become obvious.

So let’s start with numbers which are less than 20. The process of converting it to words is relatively simple, all we need to do is to look at the array index for the number (in this case, it is stored in the variable $number).

$number_in_words = $words[$number]

Great! So now we can get php to convert any number from 0 to 20 into words. So, for numbers less than 100, how should that be done? We need to find which ‘ten’ the number belongs to (twenty, thirty, forty, fifty, etc). This is quite simple too, divide the number by 10, and then floor it and multiply by 10 again (example: 49 divided by 10 = 4.9, flooring that gives 4, multiplying by 10 gives 40). Then, find the word for the ‘unit’ and join them together. I have also put the code into a function called numberToWords.

function numberToWords ($number)
{
	// the array with the number words goes here
	if ($number > 20)
	{
		$number_in_words = $words[10 * floor ($number/10)];
		$units = $number % 10;
		if ($units)
		{
			$number_in_words = $number_in_words . numberToWords ($units);
		}
	}
	else
	{
		$number_in_words = $number_in_words . " " . $words[$number];
	}
}

Cool, so now we can count to 99! So what about hundreds? Well, it’s very similar to tens. All we need to do is add another part to the function to deal with numbers greater than 100. And like the tens, we divide by 100 and take the floor. But this time, what we must do is output that number and add ‘hundred’ to it, which can be done by accessing the numbers array at index 100. It differs slightly though as in english we say something hundred AND something. So there is a check to see if there are any numbers, or if it’s just something hundred.

if ($number > 100)
{
	$number_in_words = numberToWords(floor ($number / 100)) . " " . $words[100];
	$tens = $number % 100;
	if ($tens) // check to see if we need an AND
	{
		$number_in_words = $number_in_words . " and " . numberToWords ($tens);
	}
}

Brilliant! Now we’ve taught php to count to 999! You’ll be surprised how easy it is now to count to 999,999! We can use most of what’s already written! There’s no point in writing code to output ‘seven hundred and sixty two thousand’ when we already have code which can output ‘seven hundred and sixty two’. It’s simply a case of taking the number, dividing by 1000, flooring it, then using the function which is already written to output the number, then add ‘thousand’ to it. Too good to be true, isn’t it! The only ‘difficult’ thing is working out whether a comma or ‘and’ is needed to seperate the next part of the number, but this is quite simple too. If the remaining part of the number is less than 100, we need an ‘and’ (i.e. 1001 = one thousand AND one) whereas if it’s greater than 100, we need a comma (i.e. 1101 = one thousand, one hundred and one).

if ($number > 1000)
{
	$number_in_words = $number_in_words . numberToWords(floor($number/1000)) . " " . $words[1000];
	$hundreds = $number % 1000;
	$tens = $hundreds % 100;
	if ($hundreds > 100)
	{
		$number_in_words = $number_in_words . ", " . numberToWords ($hundreds);
	}
	elseif ($tens)
	{
		$number_in_words = $number_in_words . " and " . numberToWords ($tens);
	}
}

So now we can count from 0 to 999,999. How about negative numbers? This is quite simple, if the number is negative, add ‘minus’ to the output and then negate the number.

if ($number < 0)
{
	$number = -$number;
	$number_in_words = 'minus ';
}

Here is the code for the complete function. It also includes a simple error checking if statement to ensure that the argument is a number.

function numberToWords ($number)
{
	$words = array ('zero',
			'one',
			'two',
			'three',
			'four',
			'five',
			'six',
			'seven',
			'eight',
			'nine',
			'ten',
			'eleven',
			'twelve',
			'thirteen',
			'fourteen',
			'fifteen',
			'sixteen',
			'seventeen',
			'eighteen',
			'nineteen',
			'twenty',
			30=> 'thirty',
			40 => 'fourty',
			50 => 'fifty',
			60 => 'sixty',
			70 => 'seventy',
			80 => 'eighty',
			90 => 'ninety',
			100 => 'hundred',
			1000=> 'thousand');

	if (is_numeric ($number))
	{
		$number = (int) round($number);
		if ($number < 0)
		{
			$number = -$number;
			$number_in_words = 'minus ';
		}
		if ($number > 1000)
		{
			$number_in_words = $number_in_words . numberToWords(floor($number/1000)) . " " . $words[1000];
			$hundreds = $number % 1000;
			$tens = $hundreds % 100;
			if ($hundreds > 100)
			{
				$number_in_words = $number_in_words . ", " . numberToWords ($hundreds);
			}
			elseif ($tens)
			{
				$number_in_words = $number_in_words . " and " . numberToWords ($tens);
			}
		}
		elseif ($number > 100)
		{
			$number_in_words = $number_in_words . numberToWords(floor ($number / 100)) . " " . $words[100];
			$tens = $number % 100;
			if ($tens)
			{
				$number_in_words = $number_in_words . " and " . numberToWords ($tens);
			}
		}
		elseif ($number > 20)
		{
			$number_in_words = $number_in_words . " " . $words[10 * floor ($number/10)];
			$units = $number % 10;
			if ($units)
			{
				$number_in_words = $number_in_words . numberToWords ($units);
			}
		}
		else
		{
			$number_in_words = $number_in_words . " " . $words[$number];
		}
		return $number_in_words;
	}
	return false;
}

I eventually will be writing this function to deal with decimal numbers too, so watch this space!