3v4l.org

run code in 150+ php & hhvm versions
Bugs & Features
<?php $amount = "100000000000" ; $n2w = new NumberToWords(); // not in use var_dump($n2w->convertToWords($amount)); class NumberToWords { // base 10 dictionary private $dictionary = array( 0 => 'zero', 1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five', 6 => 'six', 7 => 'seven', 8 => 'eight', 9 => 'nine', 10 => 'ten', 11 => 'eleven', 12 => 'twelve', 13 => 'thirteen', 14 => 'fourteen', 15 => 'fifteen', 16 => 'sixteen', 17 => 'seventeen', 18 => 'eighteen', 19 => 'nineteen', 20 => 'twenty', 30 => 'thirty', 40 => 'forty', 50 => 'fifty', 60 => 'sixty', 70 => 'seventy', 80 => 'eighty', 90 => 'ninety', 100 => 'hundred', 1000 => 'thousand', 1000000 => 'million', 1000000000 => 'billion', 1000000000000 => 'trillion', 1000000000000000 => 'quadrillion', 1000000000000000000 => 'quintillion' ); // irregular ordinals private $ordinal_dictionary = array( 0 => 'zeroth', 1 => 'first', 2 => 'second', 3 => 'third', 5 => 'fifth', 8 => 'eighth', 12 => 'twelfth', 20 => 'twentieth', 30 => 'thirtieth', 40 => 'fourtieth', 50 => 'fiftieth', 60 => 'sixtieth', 70 => 'seventieth', 80 => 'eightieth', 90 => 'ninetieth', 100 => 'hundredth', 1000 => 'thousandth', 1000000 => 'millionth', 1000000000 => 'billionth', 1000000000000 => 'trillionth', 1000000000000000 => 'quadrillionth', 1000000000000000000 => 'quintillionth' ); // math dictionary (not currently in use) private $math_dictionary = array( '+' => 'add', '-' => 'subtract', '/' => 'divide', '*' => 'multiply', '%' => 'modulus', '>' => 'greater than', '<' => 'less than', '=' => 'equal' ); public $hyphen = '-'; public $conjunction = 'and'; public $separator = ','; public $negative = 'negative '; // or minus public $decimal = 'point'; public $percent = 'percent'; public $degrees = 'degree'; public $currency_type = 'dollar'; public $currency_fraction_type = 'cent'; public $uppercase_words = TRUE; // will uc words except 'and' public $include_zero_dollars = TRUE; public $include_zero_cents = TRUE; const AUTOMATIC_MODE = 'automatic_mode'; const CURRENCY_MODE = 'currency_mode'; private $mode; /** * Constructor */ public function __construct() { $this->mode = self::AUTOMATIC_MODE; } /** * Sets the mode used during conversion * * @param string $mode */ public function setMode($mode) { $this->mode = $mode; } /** * Converts any number to words * * @param float $number * @param string $mode * (optional) * @return string */ public function convertToWords($number, $mode = null) { if ($mode != null) $this->mode = $mode; // default vars to null $is_negative = FALSE; $string = $fraction = null; $unit_type = ''; $support_plural_units = TRUE; // must be numeric if (!is_numeric($number)){ // strip chars that make this non-numeric // auto-detect currency and set mode (if at beginning of string) if (strpos($number, '$') == 0){ $this->mode = self::CURRENCY_MODE; } // handle simple percent (if at end of string) if (strpos($number, '%') == strlen($number) - 1){ $this->mode = self::AUTOMATIC_MODE; $unit_type .= $this->percent; $support_plural_units = FALSE; } // handle simple degrees (if at end of string) if (strpos($number, '°') == strlen($number) - 1){ $this->mode = self::AUTOMATIC_MODE; $unit_type .= $this->degrees; } // clean %, $, �, comma (,) $number = preg_replace('/[,\%\$°]/', '', $number); // if it's not clean now, it's not something we can handle if (!is_numeric($number)){ return false; } } // buffer overflow check if (($number >= 0 && (int)$number < 0) || (int)$number < 0 - PHP_INT_MAX){ trigger_error('convert only accepts numbers between -' . PHP_INT_MAX . ' and ' . PHP_INT_MAX, E_USER_WARNING); return false; } // check for negative number, if so store/remove negative and continue if ($number < 0){ $is_negative = TRUE; $number = abs($number); } if ($this->mode == self::CURRENCY_MODE){ if ($number == (int)$number){ $number .= '.00'; // force decimal by string } } // check for fraction, store it for later if (strpos($number, '.') !== FALSE){ // only apply rounding if we have a fraction if ($this->mode == self::CURRENCY_MODE && $number != (int)$number){ $number = round($number, 2); // we want .109 to round to .11 cents } list($number, $fraction) = explode('.', $number); if ($this->mode == self::CURRENCY_MODE && $fraction != null){ if (strlen($fraction) == 1){ $fraction *= 10; // we want .1 to become .10 (or 10 cents) } } } // if dealing in currency, add units (Dollars) if ($number != 0 || ($this->include_zero_dollars && $number == 0)){ $string .= $this->convert($number); // add units in currency mode if ($this->mode == self::CURRENCY_MODE){ $string .= ' ' . $this->currency_type . ($number != 1 ? 's' : '') . ''; } } // handle fraction if ($fraction !== null && is_numeric($fraction)){ if ($this->mode == self::CURRENCY_MODE){ // change mode to default so there is no unit type if (($fraction == 0 && $this->include_zero_cents) || $fraction != 0){ $before_mode = $this->mode; $this->mode = self::AUTOMATIC_MODE; // if string is not null, we want to add 'and' if ($string != null) $string .= " " . $this->conjunction . " "; $string .= $this->convert($fraction); $this->mode = $before_mode; // restore previous mode $string .= ' ' . $this->currency_fraction_type . ($fraction != 1 ? 's' : ''); } if ($this->uppercase_words) $string = $this->ucwords($string); return $string; } if ($this->mode == self::AUTOMATIC_MODE){ $string .= " " . $this->decimal . " "; // just output 'point' } $words = array(); foreach(str_split((string)$fraction) as $number ){ $words[] = $this->dictionary[$number]; } $string .= implode(' ', $words); } // handle units, if required if ($unit_type != '' && $support_plural_units && ($number != 1 || $fraction != null)) $unit_type .= 's'; // apply last minute formatting $string = ($is_negative ? $this->negative : '') . $string . " " . $unit_type; // uppercase words if set if ($this->uppercase_words) $string = $this->ucwords($string); return trim($string); } /** * Print out date/time related words * * @param string $date * @param string $pattern * @return string */ public function convertDateToWords($date, $pattern = 'l, F jS, Y') { // get the time $time = strtotime($date); // adjust the pattern for testing adding non-interpreted characters // $pattern = "l, F \u jS, Y \h"; $parts = preg_split('//', $pattern); $good_parts = array(); for($i = 0; $i < count($parts); $i++){ $part = $parts[$i]; if ($part == "\\"){ // this is an escaped character, get the character that follows as part $part = $parts[++$i]; // get next item as assignment } else{ // track 'S' (st, nd, rd, th suffix), we need the item before (if item exists and is a number) // make it read first, second, third, fourth, fifth, sixth, seventh, eighth, nineth, tenth, eleventh, twelveth... if ($part == 'S' && $i > 0){ $prev_val = date($parts[$i - 1], $time); if (is_numeric($prev_val)){ // numbers with different rules $part = $this->getNumberOrdinal($prev_val); array_pop($good_parts); // remove last item $good_parts[] = $part; // add this item continue; } } if (trim($part) != '' && $part != ',' && ($value = date($part, $time)) !== FALSE){ if ($value != ''){ // we have an equated value if (is_numeric($value)){ // this a number, let's convert it to text $good_parts[] = trim($this->convertToWords($value, self::AUTOMATIC_MODE)); } else{ // this is not a number, we don't alter it $good_parts[] = $value; } } continue; } } $good_parts[] = $part; } // echo var_export($good_parts, TRUE)."\n"; return trim(implode('', $good_parts)); } /** * Converts a number into the word-ordinal form * * @param int $number * @return string */ public function getNumberOrdinal($number) { $number = (int)abs($number); // cannot have fractions or be negative // check if number given is in ordinal dictionary if (array_key_exists($number, $this->ordinal_dictionary)){ // simple lookup in dictionary $ord = $this->ordinal_dictionary[$number]; } else{ // first remove all but the last two digits $n1 = $number % 100; // remove all but last digit unless the number is in the teens, which all should be 'th' $n2 = ($n1 < 20 ? $number : $number % 10); // check if digit less than 20 is in dictionary if (array_key_exists($n2, $this->ordinal_dictionary)){ $ord = $this->ordinal_dictionary[$n2]; if ($number > 20){ $words = $this->convertToWords($number); // we need to account for a two-part number $word_list = explode('-', $words); $word_list[count($word_list) - 1] = $ord; $ord = implode('-', $word_list); } } else{ // if not zero, use number straight across if ($n2 != 0){ $ord = $this->convert($number) . 'th'; } } } // apply this to finished product, not ord if ($this->uppercase_words) $ord = $this->ucwords($ord); return $ord; } /** * Helper method to access converting almost all words to ucfirst * * @param string $string * @return string */ private function ucwords($string) { return preg_replace_callback("/[a-zA-Z-]+/", array( &$this, 'limitedUcwords' ), $string); } /** * Called by preg callback * * @param string $match * @return string */ private function limitedUcwords($match) { $exclude = array( 'and' ); // list of words to not ucfirst if (in_array(strtolower($match[0]), $exclude)) return $match[0]; return ucfirst($match[0]); } /** * Converts a number to words regardless of other formatting * * @param int $number * @return boolean */ private function convert($number) { $string = ''; $number = (int)$number; // must be a whole number (no fraction, no strings) switch (true){ case $number < 21: $string = $this->dictionary[$number]; break; case $number < 100: $tens = ((int)($number / 10)) * 10; $units = $number % 10; $string = $this->dictionary[$tens]; if ($units){ $string .= $this->hyphen . $this->dictionary[$units]; } break; case $number < 1000: $hundreds = $number / 100; $remainder = $number % 100; $string = $this->dictionary[$hundreds] . ' ' . $this->dictionary[100]; if ($remainder){ $string .= " " . $this->conjunction . " " . $this->convert($remainder); } break; default: $baseUnit = pow(1000, floor(log($number, 1000))); $numBaseUnits = (int)($number / $baseUnit); $remainder = $number % $baseUnit; $string = $this->convert($numBaseUnits) . ' ' . $this->dictionary[$baseUnit]; if ($remainder){ $string .= $remainder < 100 ? " " . $this->conjunction . " " : $this->separator . " "; $string .= $this->convert($remainder); } break; } return $string; } }
based on Y2SFs
Output for 5.4.0 - 7.1.0
string(19) "One Hundred Billion"