Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 9 of 9
  1. #1
    Senior Coder chump2877's Avatar
    Join Date
    Dec 2004
    Location
    the U.S. of freakin' A.
    Posts
    2,744
    Thanks
    18
    Thanked 155 Times in 146 Posts

    Question Function to convert number to ordinal word

    I'm looking for a way to convert a number to an ordinal word, i.e.:

    1 --> first
    2 --> second
    11 --> eleventh
    12 --> twelfth
    20 --> twentieth
    21 --> twenty-first
    22 --> twenty-second

    ...etc., .etc...

    Any ideas? Thanks.
    Regards, R.J.

    ---------------------------------------------------------

    Help spread the word! Like my YouTube-to-Mp3 Conversion Script on Facebook !! :)
    [Related videos and tutorials are also available at my YouTube channel and on Dailymotion]
    Get free updates about new software version releases, features, and bug fixes!

  • #2
    Master Coder mlseim's Avatar
    Join Date
    Jun 2003
    Location
    Cottage Grove, Minnesota
    Posts
    9,371
    Thanks
    8
    Thanked 1,075 Times in 1,066 Posts
    I've seen several scripts like this:
    http://www.phpro.org/examples/Conver...-to-Words.html

    I wonder if there might be a built-in PHP function for that ...
    haven't found it yet though.

  • #3
    Senior Coder chump2877's Avatar
    Join Date
    Dec 2004
    Location
    the U.S. of freakin' A.
    Posts
    2,744
    Thanks
    18
    Thanked 155 Times in 146 Posts
    I found that script also when doing a Google search...but that script doesn't convert to the "ordinal word" equivalent -- just the "word" equivalent...

    The single (and I stress "single") script that I did find that attempted to do this (via Google) is so littered with errors that I didn't want to attempt to fix it without first looking for a simpler solution. Looks like I might have to try to fix it though....

    A built in PHP function would be great, but I didn't spot one either...
    Regards, R.J.

    ---------------------------------------------------------

    Help spread the word! Like my YouTube-to-Mp3 Conversion Script on Facebook !! :)
    [Related videos and tutorials are also available at my YouTube channel and on Dailymotion]
    Get free updates about new software version releases, features, and bug fixes!

  • #4
    God Emperor Fou-Lu's Avatar
    Join Date
    Sep 2002
    Location
    Saskatoon, Saskatchewan
    Posts
    16,978
    Thanks
    4
    Thanked 2,659 Times in 2,628 Posts
    Although limited in the size (number to string needs to be manually controlled at each 10 based exponential value), the ordinal is actually the easier part of it.

    Ordinal only applies on the 10's and 1's digits. So thats a plus. Ordinals for 1, 2, and 3 are st, nd, and rd. All others between 0 and 9 inclusive are th (you'll need to decide the actual '0' value as either 'zero' or 'zeroth'; we'd need our grammer nazi to verify which are valid).
    The leading digits are irrelevant with the exception being 10 through 19 which all have th ordinals (and generally need to be accommodated for particular rulesets - the alorigthm to convert 27 to 'twenty-seventh' is not the same as to convert 17 to 'seventeenth').
    PHP Code:
    header('HTTP/1.1 420 Enhance Your Calm'); 

  • #5
    Super Moderator
    Join Date
    Feb 2009
    Location
    England
    Posts
    539
    Thanks
    8
    Thanked 63 Times in 54 Posts
    You caught my imagination, and I'm now absolutely determined to do this!

    I'm going to bed now, at 1:30am... Here's what I've come up with so far:

    PHP Code:
    error_reporting(E_ALL);

    /*

    I think the best way to do this, is to create an array grouped by thousands,
    cardinal style, and lookup/modify for ordinal style.

    */

    class shortScale {
        
    // Source: Wikipedia (http://en.wikipedia.org/wiki/Names_of_large_numbers)
        
    private static $scale = array('''thousand''million''billion''trillion''quadrillion''quintillion''sextillion''octillion''nonillion''decillion''undecillion''duodecillion''tredecillion''quattuordecillion''quindecillion''sexdecillion''septendecillion''octodecillion''noverndecillion''vigintillion');
        private static 
    $digit = array('''one''two''three''four''five''six''seven''eight''nine''ten''eleven''twelve''thirteen''fourteen''fifteen''sixteen''seventeen''eighteen''nineteen');
        private static 
    $digith = array('''first''second''third''fourth''fifth''sixth''seventh''eighth''ninth''tenth''eleventh''twelfth''thirteenth''fourteenth''fiftheenth''sixteenth''seventeenth''eighteenth''nineteenth');
        private static 
    $ten = array('''''twenty''thirty''fourty''fifty''sixty''seventy''eighty''ninety');
        private static 
    $tenth = array('''''twentieth''thirtieth''fortieth''fiftieth''sixtieth''seventieth''eightieth''ninetieth');

        private static function 
    floatToArray($number, &$int, &$frac) {
            
    // Forced $number as (string), effectively to avoid (float) inprecision
            
    @list(, $frac) = explode('.'$number);
            
    $int explode(','number_format(ltrim($number'0'), 0''','));
        }
        
        private static function 
    thousandToEnglish($number) {
            
    // Gets numbers from 0 to 999 and returns the cardinal English
        
    }

        public static function 
    cardinalToOrdinal($cardinal) {
            
    // Finds the last word in the cardinal arrays and replaces it with
            // the entry from the ordinal arrays, or appends "th"
            
    $words explode(' '$cardinal);
            
    $last = &$words[count($words)-1];
            if (
    in_array($lastself::$digit)) {
                
    $last self::$digith[array_search($lastself::$digit)];
            } elseif (
    in_array($lastself::$ten)) {
                
    $last self::$tenth[array_search($lastself::$ten)];
            } elseif (
    substr($last, -2) != 'th') {
                
    $last .= 'th';
            }
            return 
    implode(' '$words);
        }

        public static function 
    toOrdinal($number) {
            
    // Converts a xth format number to English. e.g. 22nd to twenty-second.
            
    return self::cardinalToOrdinal(self::toCardinal($number));
        }
        
        public static function 
    toCardinal($number) {
            
    // Converts a number to English. e.g. 22 to twenty-two.
            
    self::floatToArray($number$int$frac);
            for(
    $i=count($int)-1$i>-1$i--) {
                echo(
    $int[$i]."\n");
                
    // use thousandToEnglish to get an array per $int array item and combine them with self::scale[$i]
            
    }
            echo(
    implode(',',$int)." and $frac\n"); 
        }
    }


    echo(
    shortScale::toOrdinal(12123456789.123)); 
    lamped.co.uk :: Design, Development & Hosting
    marcgray.co.uk :: Technical blog

  • The Following 2 Users Say Thank You to Lamped For This Useful Post:

    chump2877 (10-26-2009), FWDrew (10-26-2009)

  • #6
    Senior Coder chump2877's Avatar
    Join Date
    Dec 2004
    Location
    the U.S. of freakin' A.
    Posts
    2,744
    Thanks
    18
    Thanked 155 Times in 146 Posts
    Thanks to everyone's input on this topic, especially ComputerX who obviously spent some time thinking about how to solve this...

    I used a conglomerate of code from different places (including ComputerX's) to come up with a class that will serve my purpose for now: Get the ordinal phrases for 1 to 999. It could be done better, but it's all I really have patience for right now...

    Feel free to improve on the code if you like...

    PHP Code:
    <?
    class Number 
    {
        
    // Private Fields
        
    private $_digit = array('''one''two''three''four''five''six''seven''eight''nine''ten''eleven''twelve''thirteen''fourteen''fifteen''sixteen''seventeen''eighteen''nineteen');
        private 
    $_digith = array('''first''second''third''fourth''fifth''sixth''seventh''eighth''ninth''tenth''eleventh''twelfth''thirteenth''fourteenth''fifteenth''sixteenth''seventeenth''eighteenth''nineteenth');
        private 
    $_ten = array('''''twenty''thirty''fourty''fifty''sixty''seventy''eighty''ninety');
        private 
    $_tenth = array('''''twentieth''thirtieth''fortieth''fiftieth''sixtieth''seventieth''eightieth''ninetieth');
        
        
    // Constants
        
    const _HUNDRED 'hundred';
        const 
    _MAXNUMLENGTH 3;

        
    // Public Methods
        
    public function toOrdinal($number
        {     
            return 
    $this->cardinalToOrdinal($this->toCardinal($number));
        }
        
        
    // Private "Helper" Methods
        
    private function cardinalToOrdinal($cardinal
        {
            
    // Finds the last word in the cardinal arrays and replaces it with
            // the entry from the ordinal arrays, or appends "th"
            
    $words explode(' '$cardinal);
            
    $last $words[count($words)-1];
            
    $last_parts explode('-'$last);
            
    $last_part $last_parts[count($last_parts)-1];
            
            if (
    in_array($last_part$this->_digit)) 
            {
                
    $last_part $this->_digith[array_search($last_part$this->_digit)];
            } 
            elseif (
    in_array($last_part$this->_ten)) 
            {
                
    $last_part $this->_tenth[array_search($last_part$this->_ten)];
            } 
            elseif (
    $last_part == self::_HUNDRED
            {
                
    $last_part self::_HUNDRED.'th';
            }
            
    $last_parts[count($last_parts)-1] = $last_part;
            
    $words[count($words)-1] = implode('-'$last_parts);
            return 
    implode(' '$words);
        }    
        
        private function 
    toCardinal($number
        {
            if (
    $number || $number > (int)(str_repeat('9'self::_MAXNUMLENGTH))) 
            { 
                throw new 
    Exception("Number is out of range");
            } 

            
    $Hn floor($number 100);      /* Hundreds */ 
            
    $number -= $Hn 100
            
    $Dn floor($number 10);       /* Tens */ 
            
    $n $number 10;               /* Ones */ 

            
    $res ""

            if (
    $Hn
            { 
                
    $res .= (empty($res) ? "" " ") . 
                    
    $this->toCardinal($Hn) . " " self::_HUNDRED
            }
            
            if (
    $Dn || $n
            { 
                if (!empty(
    $res)) 
                { 
                    
    $res .= " and "
                } 

                if (
    $Dn 2
                { 
                    
    $res .= $this->_digit[$Dn 10 $n]; 
                } 
                else 
                { 
                    
    $res .= $this->_ten[$Dn]; 

                    if (
    $n
                    { 
                        
    $res .= "-" $this->_digit[$n]; 
                    } 
                } 
            } 
            
            return 
    $res;
        }
    }

    $num = new Number;
    for (
    $i=1$i<2000$i++)
    {
        try 
        {
            echo 
    $num->toOrdinal($i) . '<br />';
        }
        catch(
    Exception $ex)
        {
            die(
    $ex->getMessage());
        }
    }
    ?>
    Regards, R.J.

    ---------------------------------------------------------

    Help spread the word! Like my YouTube-to-Mp3 Conversion Script on Facebook !! :)
    [Related videos and tutorials are also available at my YouTube channel and on Dailymotion]
    Get free updates about new software version releases, features, and bug fixes!

  • #7
    Super Moderator
    Join Date
    Feb 2009
    Location
    England
    Posts
    539
    Thanks
    8
    Thanked 63 Times in 54 Posts
    I'm really pleased you got something working. I've continued working on it for the sake of completion, and that I like having useful code snippets.

    This example is perfect for numbers from 0 to 999, with an example of cardinal and ordinal useage. I don't put a hyphen in, as it requires extra processing from the cardinalToOrdinal function that I don't personally think is necessary.

    I'll put in the scaling and update here, again, for completeness sake.

    PHP Code:
    error_reporting(E_ALL);


    class 
    shortScale {
        
    // Source: Wikipedia (http://en.wikipedia.org/wiki/Names_of_large_numbers)
        
    private static $scale = array('''thousand''million''billion''trillion''quadrillion''quintillion''sextillion''octillion''nonillion''decillion''undecillion''duodecillion''tredecillion''quattuordecillion''quindecillion''sexdecillion''septendecillion''octodecillion''noverndecillion''vigintillion');
        private static 
    $digit = array('''one''two''three''four''five''six''seven''eight''nine''ten''eleven''twelve''thirteen''fourteen''fifteen''sixteen''seventeen''eighteen''nineteen');
        private static 
    $digith = array('''first''second''third''fourth''fifth''sixth''seventh''eighth''ninth''tenth''eleventh''twelfth''thirteenth''fourteenth''fiftheenth''sixteenth''seventeenth''eighteenth''nineteenth');
        private static 
    $ten = array('''''twenty''thirty''fourty''fifty''sixty''seventy''eighty''ninety');
        private static 
    $tenth = array('''''twentieth''thirtieth''fortieth''fiftieth''sixtieth''seventieth''eightieth''ninetieth');

        private static function 
    floatToArray($number, &$int, &$frac) {
            
    // Forced $number as (string), effectively to avoid (float) inprecision
            
    @list(, $frac) = explode('.'$number);
            
    $int explode(','number_format(ltrim($number'0'), 0''','));
        }
        
        static function 
    thousandToEnglish($number) {
            
    // Gets numbers from 0 to 999 and returns the cardinal English
            
    $hundreds floor($number 100);
            
    $tens $number 100;
            
    $pre = ($hundreds self::$digit[$hundreds].' hundred' '');
            if (
    $tens 20)
                
    $post self::$digit[$tens];
            else
                
    $post trim(self::$ten[floor($tens 10)].' '.self::$digit[$tens 10]);
            if (
    $pre && $post) return $pre.' and '.$post;
            return 
    $pre.$post;
        }

        public static function 
    cardinalToOrdinal($cardinal) {
            
    // Finds the last word in the cardinal arrays and replaces it with
            // the entry from the ordinal arrays, or appends "th"
            
    $words explode(' '$cardinal);
            
    $last = &$words[count($words)-1];
            if (
    in_array($lastself::$digit)) {
                
    $last self::$digith[array_search($lastself::$digit)];
            } elseif (
    in_array($lastself::$ten)) {
                
    $last self::$tenth[array_search($lastself::$ten)];
            } elseif (
    substr($last, -2) != 'th') {
                
    $last .= 'th';
            }
            return 
    implode(' '$words);
        }

        public static function 
    toOrdinal($number) {
            
    // Converts a xth format number to English. e.g. 22nd to twenty-second.
            
    return self::cardinalToOrdinal(self::toCardinal($number));
        }
        
        public static function 
    toCardinal($number) {
            
    // Converts a number to English. e.g. 22 to twenty-two.
            
    self::floatToArray($number$int$frac);
            for(
    $i=count($int)-1$i>-1$i--) {
                echo(
    $int[$i]."\n");
                
    // use thousandToEnglish to get an array per $int array item and combine them with self::scale[$i]
            
    }
            echo(
    implode(',',$int)." and $frac\n"); 
        }
    }
    for (
    $i=0$i<20$i++) {
        
    $r rand(0,999);
        
    $cardinal shortScale::thousandToEnglish($r);
        echo(
    $r.': '.$cardinal.' -- '.shortScale::cardinalToOrdinal($cardinal)."\n");

    lamped.co.uk :: Design, Development & Hosting
    marcgray.co.uk :: Technical blog

  • #8
    Super Moderator
    Join Date
    Feb 2009
    Location
    England
    Posts
    539
    Thanks
    8
    Thanked 63 Times in 54 Posts
    Right, I'm done.

    Did you know that 13,052,149,762,014,642,176,925,499,392 is thirteen nonillion, fifty two octillion, one hundred and fourty nine sextillion, seven hundred and sixty two quintillion, fourteen quadrillion, six hundred and fourty two trillion, one hundred and seventy six billion, nine hundred and twenty five million, four hundred and ninety nine thousand and three hundred and ninety two?

    Use:

    PHP Code:
    $english shortScale::toOrdinal($number); // 2 to second
    $english shortScale::toCardinal($number); // 2 to two 
    The class:
    PHP Code:
    class shortScale {
        
    // Source: Wikipedia (http://en.wikipedia.org/wiki/Names_of_large_numbers)
        
    private static $scale = array('''thousand''million''billion''trillion''quadrillion''quintillion''sextillion''octillion''nonillion''decillion''undecillion''duodecillion''tredecillion''quattuordecillion''quindecillion''sexdecillion''septendecillion''octodecillion''noverndecillion''vigintillion');
        private static 
    $digit = array('''one''two''three''four''five''six''seven''eight''nine''ten''eleven''twelve''thirteen''fourteen''fifteen''sixteen''seventeen''eighteen''nineteen');
        private static 
    $digith = array('''first''second''third''fourth''fifth''sixth''seventh''eighth''ninth''tenth''eleventh''twelfth''thirteenth''fourteenth''fiftheenth''sixteenth''seventeenth''eighteenth''nineteenth');
        private static 
    $ten = array('''''twenty''thirty''fourty''fifty''sixty''seventy''eighty''ninety');
        private static 
    $tenth = array('''''twentieth''thirtieth''fortieth''fiftieth''sixtieth''seventieth''eightieth''ninetieth');

        private static function 
    floatToArray($number, &$int, &$frac) {
            
    // Forced $number as (string), effectively to avoid (float) inprecision
            
    @list(, $frac) = explode('.'$number);
            if (
    $frac || !is_numeric($number) || (strlen($number) > 60)) throw new Exception('Not a number or not a supported number type');
            
    // $int = explode(',', number_format(ltrim($number, '0'), 0, '', ',')); -- Buggy
            
    $int str_split(str_pad($numberceil(strlen($number)/3)*3'0'STR_PAD_LEFT), 3);
        }
        
        private static function 
    thousandToEnglish($number) {
            
    // Gets numbers from 0 to 999 and returns the cardinal English
            
    $hundreds floor($number 100);
            
    $tens $number 100;
            
    $pre = ($hundreds self::$digit[$hundreds].' hundred' '');
            if (
    $tens 20)
                
    $post self::$digit[$tens];
            else
                
    $post trim(self::$ten[floor($tens 10)].' '.self::$digit[$tens 10]);
            if (
    $pre && $post) return $pre.' and '.$post;
            return 
    $pre.$post;
        }

        private static function 
    cardinalToOrdinal($cardinal) {
            
    // Finds the last word in the cardinal arrays and replaces it with
            // the entry from the ordinal arrays, or appends "th"
            
    $words explode(' '$cardinal);
            
    $last = &$words[count($words)-1];
            if (
    in_array($lastself::$digit)) {
                
    $last self::$digith[array_search($lastself::$digit)];
            } elseif (
    in_array($lastself::$ten)) {
                
    $last self::$tenth[array_search($lastself::$ten)];
            } elseif (
    substr($last, -2) != 'th') {
                
    $last .= 'th';
            }
            return 
    implode(' '$words);
        }

        public static function 
    toOrdinal($number) {
            
    // Converts a xth format number to English. e.g. 22nd to twenty-second.
            
    return trim(self::cardinalToOrdinal(self::toCardinal($number)));
        }
        
        public static function 
    toCardinal($number) {
            
    // Converts a number to English. e.g. 22 to twenty-two.
            
    self::floatToArray($number$int$frac);
            
    $int array_reverse($int);
            for(
    $i=count($int)-1$i>-1$i--) {
                
    $englishnumber self::thousandToEnglish($int[$i]);
                if (
    $englishnumber
                    
    $english[] = $englishnumber.' '.self::$scale[$i];
            }
            
    $post array_pop($english);
            
    $pre implode(', '$english);
            if (
    $pre && $post) return trim($pre.' and '.$post);
            return 
    trim($pre.$post);
        }

    Extreme example:
    PHP Code:
    for ($i=0$i<20$i++) {
        
    $a rand(0,PHP_INT_MAX);
        
    $b rand(0,PHP_INT_MAX);
        
    $c rand(0,PHP_INT_MAX);
        
    $huge $a.$b.$c;
        echo(
    $huge."\n".shortScale::toOrdinal($huge)."\n\n");

    Let me know if you find a bug. If you don't like the spelling of my native English fingers, change the arrays yourself!

    Edit: Bug fix already: Now supports numbers like one billion and one, and reports an error on numbers over 60 digits.
    Last edited by Lamped; 10-26-2009 at 02:42 PM. Reason: Bug fix
    lamped.co.uk :: Design, Development & Hosting
    marcgray.co.uk :: Technical blog

  • #9
    Regular Coder mOrloff's Avatar
    Join Date
    Nov 2008
    Location
    The Great Pacific NW, USA
    Posts
    422
    Thanks
    8
    Thanked 6 Times in 6 Posts
    SWEET!!!
    Regardless of the fact that this thread is almost 4 years old, it saved me some real headache (and time).

    I needed to build a number-word "dictionary", and with this class, I was able to bust that out in nothing flat =D
    Since I needed to include the shortened versions of ordinals too, I added this method.
    Hope someone else finds it useful.

    PHP Code:
        public static function toShortOrdinal($number) {
            
    // Converts a number to the shortened English (alpha-numeric) ordinal. e.g. 23 to 23rd.
            
    return $number substr(self::cardinalToOrdinal(self::toCardinal($number)),-2);
        } 
    Also, in the comment for toOrdinal(), it ways that it "Converts a xth format number to English. e.g. 22nd to twenty-second", but when I tested shortScale::toOrdinal('3rd'), it triggered the "Not a number or not a supported number type" error.
    Am I missing something???

    ~ Mo
    Last edited by mOrloff; 03-01-2013 at 02:47 PM.
    ...because it's dundant already.


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •