Spelling of ordinal numbers

From Rosetta Code
Task
Spelling of ordinal numbers
You are encouraged to solve this task according to the task description, using any language you may know.

Ordinal numbers   (as used in this Rosetta Code task),   are numbers that describe the   position   of something in a list.

It is this context that ordinal numbers will be used, using an English-spelled name of an ordinal number.


The ordinal numbers are   (at least, one form of them):

  1st  2nd  3rd  4th  5th  6th  7th  ···  99th  100th  ···  1000000000th  ···  etc

sometimes expressed as:

  1st  2nd  3rd  4th  5th  6th  7th  ···  99th  100th  ···  1000000000th  ···


For this task, the following (English-spelled form) will be used:

  first second third fourth fifth sixth seventh ninety-nineth one hundredth one billionth


Furthermore, the short scale numbering system (i.e. 2,000,000,000 is two billion) will be used here. wp:Long and short scales

2,000,000,000   is two billion,   not   two milliard.


Task

Write a driver and a function (subroutine/routine ···) that returns the English-spelled ordinal version of a specified number   (a positive integer).

Optionally, try to support as many forms of an integer that can be expressed:   123   00123.0   1.23e2   all are forms of the same integer.

Show all output here.


Test cases

Use (at least) the test cases of:

  1  2  3  4  5  11  65  100  101  272  23456  8007006005004003


Related tasks



ALGOL 68

Assumes LONG INT is at least 64 bits, as in e.g., Algol 68G.

BEGIN # construct number names from a number                                  #
    []STRING units
           = ( "one", "two",  "three", "four", "five", "six", "seven", "eight", "nine" );
    []STRING unit prefix
           = ( "ten", "twen", "thir",  "four", "fif", "six",  "seven", "eigh",  "nine" );
    []STRING singleth
           = ( "first", "second",  "third",  "fourth", "fifth"
             , "sixth", "seventh", "eighth", "nineth"
             );
    []STRING power suffix
           = ( "thousand", "million", "billion", "trillion", "quadrillion", "quintillion" );
    # returns n converted to a number name, n must be 1-99                    #
    #         if final is TRUE, the name will end in st, nd, rd, ...          #
    PROC two digits = ( INT n, BOOL final )STRING:
         IF   n < 10       THEN IF final THEN singleth[ n ] ELSE units[ n ] FI
         ELIF n = 10       THEN IF final THEN "tenth"       ELSE "ten"      FI
         ELIF n = 11       THEN IF final THEN "eleventh"    ELSE "eleven"   FI
         ELIF n = 12       THEN IF final THEN "twelfth"     ELSE "twelve"   FI
         ELIF n < 20       THEN unit prefix[ n - 10 ]
                              + IF final THEN "teenth" ELSE "teen" FI
         ELIF n MOD 10 = 0 THEN unit prefix[ n OVER 10 ]
                              + IF final THEN "tieth"  ELSE "ty"   FI
         ELSE unit prefix[ n OVER 10 ]
            + "ty "
            + IF final THEN singleth[ n MOD 10 ] ELSE units[ n MOD 10 ] FI
         FI # two digits # ;
    # returns n converted to a number name, n must be 1-999                   #
    #         if final is TRUE, the name will end in st, nd, rd, ...          #
    PROC three digits = ( INT n, BOOL final )STRING:
         IF   n < 100
         THEN two digits( n, final )
         ELIF STRING hundreds = units[ n OVER 100 ] + " hundred";
              INT    ending = n MOD 100;
              ending = 0
         THEN IF final THEN hundreds + "th" ELSE hundreds FI
         ELSE hundreds + " and " + two digits( ending, final )
         FI # three digits # ;
    # returns the "name" of n                                                 #
    OP NAME = ( LONG INT n )STRING:
       IF   n < 0 THEN "minus " + NAME - n
       ELIF n = 0 THEN "zeroth"
       ELSE
            # have a positive number to name                                  #
            LONG INT v         := n;
            STRING   result    := "";
            INT      power pos := 0;
            WHILE v /= 0 DO
                BOOL     final component = power pos = 0;
                INT v999 = SHORTEN ( v MOD 1000 );
                IF v999 /= 0 THEN
                    STRING component := three digits( v999, final component );
                    IF power pos > 0 THEN
                        component +:= " " + power suffix[ power pos ];
                        IF final component THEN component +:= "th" FI
                    FI;
                    IF power pos = 0 AND v > 1000 AND v999 < 100 THEN
                        "and " +=: component
                    FI;
                    IF result /= "" THEN component +:= " " FI;
                    component +=: result
                FI;
                power pos +:= 1;
                v OVERAB 1000
            OD;
            IF n MOD 1000 = 0 THEN result + "th" ELSE result FI
       FI # NAME # ;
    # additional operator to handle shorter integers                          #
    OP NAME = ( INT       n )STRING: NAME LENG n;
    # additional operators to handle integers expressed in floating point     #
    OP NAME = ( LONG REAL n )STRING: NAME ENTIER n;
    OP NAME = ( REAL      n )STRING: NAME ENTIER n;
    # task test cases                                                         #
    []LONG INT t = ( 1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23 456, 8 007 006 005 004 003 );
    FOR n FROM LWB t TO UPB t DO
        print( ( whole( t[ n ], -16 ), ": ", NAME t[ n ], newline ) )
    OD
END
Output:
               1: first
               2: second
               3: third
               4: fourth
               5: fifth
              11: eleventh
              65: sixty fifth
             100: one hundredth
             101: one hundred and first
             272: two hundred and seventy second
           23456: twenty three thousand four hundred and fifty sixth
8007006005004003: eight quadrillion seven trillion six billion five million four thousand and third

AutoHotkey

Based on Number_names

OrdinalNumber(n){
	OrdinalNumber := {"one":"first", "two":"second", "three":"third", "five":"fifth", "eight":"eighth", "nine":"ninth", "twelve": "twelfth"}
	RegExMatch(n, "\w+$", m)
	return (OrdinalNumber[m] ? RegExReplace(n, "\w+$", OrdinalNumber[m]) : n "th")
}

Spell(n) { ; recursive function to spell out the name of a max 36 digit integer, after leading 0s removed 
    Static p1=" thousand ",p2=" million ",p3=" billion ",p4=" trillion ",p5=" quadrillion ",p6=" quintillion " 
         , p7=" sextillion ",p8=" septillion ",p9=" octillion ",p10=" nonillion ",p11=" decillion " 
         , t2="twenty",t3="thirty",t4="forty",t5="fifty",t6="sixty",t7="seventy",t8="eighty",t9="ninety" 
         , o0="zero",o1="one",o2="two",o3="three",o4="four",o5="five",o6="six",o7="seven",o8="eight" 
         , o9="nine",o10="ten",o11="eleven",o12="twelve",o13="thirteen",o14="fourteen",o15="fifteen" 
         , o16="sixteen",o17="seventeen",o18="eighteen",o19="nineteen"
		 
    n :=RegExReplace(n,"^0+(\d)","$1") ; remove leading 0s from n 
    If  (11 < d := (StrLen(n)-1)//3)   ; #of digit groups of 3 
        Return "Number too big"
    If (d)                             ; more than 3 digits 1000+ 
        Return Spell(SubStr(n,1,-3*d)) p%d% ((s:=SubStr(n,1-3*d)) ? ", " Spell(s) : "") 
    i := SubStr(n,1,1) 
    If (n > 99)                        ; 3 digits 100..999
        Return o%i% " hundred" ((s:=SubStr(n,2)) ? " and " Spell(s) : "") 
    If (n > 19)                        ; n = 20..99 
        Return t%i% ((o:=SubStr(n,2)) ? "-" o%o% : "") 
    Return o%n%                        ; n = 0..19 
} 

PrettyNumber(n) { ; inserts thousands separators into a number string 
    Return RegExReplace( RegExReplace(n,"^0+(\d)","$1"), "\G\d+?(?=(\d{3})+(?:\D|$))", "$0,")
}
Example:
for i, n in StrSplit("1 2 3 4 5 11 65 100 101 272 23456 8007006005004003", " ")
    res .= PrettyNumber(n) "`t" Spell(n) "`t" OrdinalNumber(Spell(n)) "`n"
MsgBox % res
Outputs:
1	first
2	second
3	third
4	fourth
5	fifth
11	eleventh
65	sixty-fifth
100	one hundredth
101	one hundred and first
272	two hundred and seventy-second
23,456	twenty-three thousand , four hundred and fifty-sixth
8,007,006,005,004,003	eight quadrillion , seven trillion , six billion , five million , four thousand , third

C

Library: GLib
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <glib.h>

typedef uint64_t integer;

typedef struct number_names_tag {
    const char* cardinal;
    const char* ordinal;
} number_names;

const number_names small[] = {
    { "zero", "zeroth" }, { "one", "first" }, { "two", "second" },
    { "three", "third" }, { "four", "fourth" }, { "five", "fifth" },
    { "six", "sixth" }, { "seven", "seventh" }, { "eight", "eighth" },
    { "nine", "ninth" }, { "ten", "tenth" }, { "eleven", "eleventh" },
    { "twelve", "twelfth" }, { "thirteen", "thirteenth" },
    { "fourteen", "fourteenth" }, { "fifteen", "fifteenth" },
    { "sixteen", "sixteenth" }, { "seventeen", "seventeenth" },
    { "eighteen", "eighteenth" }, { "nineteen", "nineteenth" }
};

const number_names tens[] = {
    { "twenty", "twentieth" }, { "thirty", "thirtieth" },
    { "forty", "fortieth" }, { "fifty", "fiftieth" },
    { "sixty", "sixtieth" }, { "seventy", "seventieth" },
    { "eighty", "eightieth" }, { "ninety", "ninetieth" }
};

typedef struct named_number_tag {
    const char* cardinal;
    const char* ordinal;
    integer number;
} named_number;

const named_number named_numbers[] = {
    { "hundred", "hundredth", 100 },
    { "thousand", "thousandth", 1000 },
    { "million", "millionth", 1000000 },
    { "billion", "billionth", 1000000000 },
    { "trillion", "trillionth", 1000000000000 },
    { "quadrillion", "quadrillionth", 1000000000000000ULL },
    { "quintillion", "quintillionth", 1000000000000000000ULL }
};

const char* get_small_name(const number_names* n, bool ordinal) {
    return ordinal ? n->ordinal : n->cardinal;
}

const char* get_big_name(const named_number* n, bool ordinal) {
    return ordinal ? n->ordinal : n->cardinal;
}

const named_number* get_named_number(integer n) {
    const size_t names_len = sizeof(named_numbers)/sizeof(named_numbers[0]);
    for (size_t i = 0; i + 1 < names_len; ++i) {
        if (n < named_numbers[i + 1].number)
            return &named_numbers[i];
    }
    return &named_numbers[names_len - 1];
}

void append_number_name(GString* gstr, integer n, bool ordinal) {
    if (n < 20)
        g_string_append(gstr, get_small_name(&small[n], ordinal));
    else if (n < 100) {
        if (n % 10 == 0) {
            g_string_append(gstr, get_small_name(&tens[n/10 - 2], ordinal));
        } else {
            g_string_append(gstr, get_small_name(&tens[n/10 - 2], false));
            g_string_append_c(gstr, '-');
            g_string_append(gstr, get_small_name(&small[n % 10], ordinal));
        }
    } else {
        const named_number* num = get_named_number(n);
        integer p = num->number;
        append_number_name(gstr, n/p, false);
        g_string_append_c(gstr, ' ');
        if (n % p == 0) {
            g_string_append(gstr, get_big_name(num, ordinal));
        } else {
            g_string_append(gstr, get_big_name(num, false));
            g_string_append_c(gstr, ' ');
            append_number_name(gstr, n % p, ordinal);
        }
    }
}

GString* number_name(integer n, bool ordinal) {
    GString* result = g_string_sized_new(8);
    append_number_name(result, n, ordinal);
    return result;
}

void test_ordinal(integer n) {
    GString* name = number_name(n, true);
    printf("%llu: %s\n", n, name->str);
    g_string_free(name, TRUE);
}

int main() {
    test_ordinal(1);
    test_ordinal(2);
    test_ordinal(3);
    test_ordinal(4);
    test_ordinal(5);
    test_ordinal(11);
    test_ordinal(15);
    test_ordinal(21);
    test_ordinal(42);
    test_ordinal(65);
    test_ordinal(98);
    test_ordinal(100);
    test_ordinal(101);
    test_ordinal(272);
    test_ordinal(300);
    test_ordinal(750);
    test_ordinal(23456);
    test_ordinal(7891233);
    test_ordinal(8007006005004003LL);
    return 0;
}
Output:
1: first
2: second
3: third
4: fourth
5: fifth
11: eleventh
15: fifteenth
21: twenty-first
42: forty-second
65: sixty-fifth
98: ninety-eighth
100: one hundredth
101: one hundred first
272: two hundred seventy-second
300: three hundredth
750: seven hundred fiftieth
23456: twenty-three thousand four hundred fifty-sixth
7891233: seven million eight hundred ninety-one thousand two hundred thirty-third
8007006005004003: eight quadrillion seven trillion six billion five million four thousand third

C#

Translation of: Java
using System;
using System.Collections.Generic;

class SpellingOfOrdinalNumbers
{
    private static readonly Dictionary<string, string> ordinalMap = new Dictionary<string, string>
    {
        {"one", "first"},
        {"two", "second"},
        {"three", "third"},
        {"five", "fifth"},
        {"eight", "eighth"},
        {"nine", "ninth"},
        {"twelve", "twelfth"}
    };

    static void Main(string[] args)
    {
        long[] tests = new long[] { 1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 8007006005004003L };
        foreach (long test in tests)
        {
            Console.WriteLine($"{test} = {ToOrdinal(test)}");
        }
    }

    private static string ToOrdinal(long n)
    {
        string spelling = NumToString(n);
        string[] split = spelling.Split(' ');
        string last = split[split.Length - 1];
        string replace;
        if (last.Contains("-"))
        {
            string[] lastSplit = last.Split('-');
            string lastWithDash = lastSplit[1];
            string lastReplace;
            if (ordinalMap.ContainsKey(lastWithDash))
            {
                lastReplace = ordinalMap[lastWithDash];
            }
            else if (lastWithDash.EndsWith("y"))
            {
                lastReplace = lastWithDash.Substring(0, lastWithDash.Length - 1) + "ieth";
            }
            else
            {
                lastReplace = lastWithDash + "th";
            }
            replace = lastSplit[0] + "-" + lastReplace;
        }
        else
        {
            if (ordinalMap.ContainsKey(last))
            {
                replace = ordinalMap[last];
            }
            else if (last.EndsWith("y"))
            {
                replace = last.Substring(0, last.Length - 1) + "ieth";
            }
            else
            {
                replace = last + "th";
            }
        }
        split[split.Length - 1] = replace;
        return string.Join(" ", split);
    }

    private static readonly string[] nums = new string[]
    {
        "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
        "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
    };

    private static readonly string[] tens = new string[] { "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };

    private static string NumToString(long n)
    {
        return NumToStringHelper(n);
    }

    private static string NumToStringHelper(long n)
    {
        if (n < 0)
        {
            return "negative " + NumToStringHelper(-n);
        }
        if (n <= 19)
        {
            return nums[n];
        }
        if (n <= 99)
        {
            return tens[n / 10] + (n % 10 > 0 ? "-" + NumToStringHelper(n % 10) : "");
        }
        string label = null;
        long factor = 0;
        if (n <= 999)
        {
            label = "hundred";
            factor = 100;
        }
        else if (n <= 999999)
        {
            label = "thousand";
            factor = 1000;
        }
        else if (n <= 999999999)
        {
            label = "million";
            factor = 1000000;
        }
        else if (n <= 999999999999L)
        {
            label = "billion";
            factor = 1000000000;
        }
        else if (n <= 999999999999999L)
        {
            label = "trillion";
            factor = 1000000000000L;
        }
        else if (n <= 999999999999999999L)
        {
            label = "quadrillion";
            factor = 1000000000000000L;
        }
        else
        {
            label = "quintillion";
            factor = 1000000000000000000L;
        }
        return NumToStringHelper(n / factor) + " " + label + (n % factor > 0 ? " " + NumToStringHelper(n % factor) : "");
    }
}
Output:
1 = first
2 = second
3 = third
4 = fourth
5 = fifth
11 = eleventh
65 = sixty-fifth
100 = one hundredth
101 = one hundred first
272 = two hundred seventy-second
23456 = twenty-three thousand four hundred fifty-sixth
8007006005004003 = eight quadrillion seven trillion six billion five million four thousand third

C++

#include <iostream>
#include <string>
#include <cstdint>

typedef std::uint64_t integer;

struct number_names {
    const char* cardinal;
    const char* ordinal;
};

const number_names small[] = {
    { "zero", "zeroth" }, { "one", "first" }, { "two", "second" },
    { "three", "third" }, { "four", "fourth" }, { "five", "fifth" },
    { "six", "sixth" }, { "seven", "seventh" }, { "eight", "eighth" },
    { "nine", "ninth" }, { "ten", "tenth" }, { "eleven", "eleventh" },
    { "twelve", "twelfth" }, { "thirteen", "thirteenth" },
    { "fourteen", "fourteenth" }, { "fifteen", "fifteenth" },
    { "sixteen", "sixteenth" }, { "seventeen", "seventeenth" },
    { "eighteen", "eighteenth" }, { "nineteen", "nineteenth" }
};

const number_names tens[] = {
    { "twenty", "twentieth" }, { "thirty", "thirtieth" },
    { "forty", "fortieth" }, { "fifty", "fiftieth" },
    { "sixty", "sixtieth" }, { "seventy", "seventieth" },
    { "eighty", "eightieth" }, { "ninety", "ninetieth" }
};

struct named_number {
    const char* cardinal;
    const char* ordinal;
    integer number;
};

const named_number named_numbers[] = {
    { "hundred", "hundredth", 100 },
    { "thousand", "thousandth", 1000 },
    { "million", "millionth", 1000000 },
    { "billion", "billionth", 1000000000 },
    { "trillion", "trillionth", 1000000000000 },
    { "quadrillion", "quadrillionth", 1000000000000000ULL },
    { "quintillion", "quintillionth", 1000000000000000000ULL }
};

const char* get_name(const number_names& n, bool ordinal) {
    return ordinal ? n.ordinal : n.cardinal;
}

const char* get_name(const named_number& n, bool ordinal) {
    return ordinal ? n.ordinal : n.cardinal;
}

const named_number& get_named_number(integer n) {
    constexpr size_t names_len = std::size(named_numbers);
    for (size_t i = 0; i + 1 < names_len; ++i) {
        if (n < named_numbers[i + 1].number)
            return named_numbers[i];
    }
    return named_numbers[names_len - 1];
}

std::string number_name(integer n, bool ordinal) {
    std::string result;
    if (n < 20)
        result = get_name(small[n], ordinal);
    else if (n < 100) {
        if (n % 10 == 0) {
            result = get_name(tens[n/10 - 2], ordinal);
        } else {
            result = get_name(tens[n/10 - 2], false);
            result += "-";
            result += get_name(small[n % 10], ordinal);
        }
    } else {
        const named_number& num = get_named_number(n);
        integer p = num.number;
        result = number_name(n/p, false);
        result += " ";
        if (n % p == 0) {
            result += get_name(num, ordinal);
        } else {
            result += get_name(num, false);
            result += " ";
            result += number_name(n % p, ordinal);
        }
    }
    return result;
}

void test_ordinal(integer n) {
    std::cout << n << ": " << number_name(n, true) << '\n';
}

int main() {
    test_ordinal(1);
    test_ordinal(2);
    test_ordinal(3);
    test_ordinal(4);
    test_ordinal(5);
    test_ordinal(11);
    test_ordinal(15);
    test_ordinal(21);
    test_ordinal(42);
    test_ordinal(65);
    test_ordinal(98);
    test_ordinal(100);
    test_ordinal(101);
    test_ordinal(272);
    test_ordinal(300);
    test_ordinal(750);
    test_ordinal(23456);
    test_ordinal(7891233);
    test_ordinal(8007006005004003LL);
    return 0;
}
Output:
1: first
2: second
3: third
4: fourth
5: fifth
11: eleventh
15: fifteenth
21: twenty-first
42: forty-second
65: sixty-fifth
98: ninety-eighth
100: one hundredth
101: one hundred first
272: two hundred seventy-second
300: three hundredth
750: seven hundred fiftieth
23456: twenty-three thousand four hundred fifty-sixth
7891233: seven million eight hundred ninety-one thousand two hundred thirty-third
8007006005004003: eight quadrillion seven trillion six billion five million four thousand third

Clojure

(def test-cases [1 2 3 4 5 11 65 100 101 272 23456 8007006005004003])
(pprint
  (sort (zipmap test-cases (map #(clojure.pprint/cl-format nil "~:R" %) test-cases))))
Output:
([1 "first"]
 [2 "second"]
 [3 "third"]
 [4 "fourth"]
 [5 "fifth"]
 [11 "eleventh"]
 [65 "sixty-fifth"]
 [100 "one hundredth"]
 [101 "one hundred first"]
 [272 "two hundred seventy-second"]
 [23456 "twenty-three thousand, four hundred fifty-sixth"]
 [8007006005004003
  "eight quadrillion, seven trillion, six billion, five million, four thousand, third"])

Common Lisp

Common Lisp's format is able to do this directly. Here's a short function wrapping it and a demonstration.

(defun ordinal-number (n)
  (format nil "~:R" n))

#|
CL-USER> (loop for i in '(1 2 3 4 5 11 65 100 101 272 23456 8007006005004003)
               do (format t "~a: ~a~%" i (ordinal-number i)))
1: first
2: second
3: third
4: fourth
5: fifth
11: eleventh
65: sixty-fifth
100: one hundredth
101: one hundred first
272: two hundred seventy-second
23456: twenty-three thousand four hundred fifty-sixth
8007006005004003: eight quadrillion seven trillion six billion five million four thousand third
NIL
|#

Factor

Factor's math.text.english vocabulary provides the number>text word for converting numbers to written English. It also provides the ordinal-suffix word for getting the suffix for a given number, such as th for 12. The bulk of this code deals with converting the output of number>text to ordinal format.

USING: assocs formatting grouping kernel literals locals math
math.parser math.text.english qw regexp sequences
splitting.extras ;
IN: rosetta-code.spelling-ordinal-numbers

<PRIVATE

! Factor supports the arbitrary use of commas in integer
! literals, as some number systems (e.g. Indian) don't solely
! break numbers up into triplets.

CONSTANT: test-cases qw{
    1 2 3 4 5 11 65 100 101 272 23456 8007006005004003 123
    00123.0 1.23e2 1,2,3 0b1111011 0o173 0x7B 2706/22
}

CONSTANT: replacements $[
    qw{
        one    first
        two    second
        three  third
        five   fifth
        eight  eighth
        nine   ninth
        twelve twelfth
    } 2 group
]

: regular-ordinal ( n -- str )
    [ number>text ] [ ordinal-suffix ] bi append ;

! Since Factor's number>text word contains commas and "and",
! strip them out with a regular expression.
  
: text>ordinal-text ( str -- str' ) R/ \sand|,/ "" re-replace ;

PRIVATE>
    
:: number>ordinal-text ( n! -- str )
    n >integer n!
    n number>text " ,-" split* dup last replacements at
    [ [ but-last ] dip suffix "" join ]
    [ drop n regular-ordinal          ] if* text>ordinal-text ;

<PRIVATE
    
: print-ordinal-pair ( str x -- )
    number>ordinal-text "%16s => %s\n" printf ;
    
PRIVATE>

: ordinal-text-demo ( -- )
    test-cases [ dup string>number print-ordinal-pair ] each
    "C{ 123 0 }" C{ 123 0 } print-ordinal-pair ;
    
MAIN: ordinal-text-demo
Output:
               1 => first
               2 => second
               3 => third
               4 => fourth
               5 => fifth
              11 => eleventh
              65 => sixty-fifth
             100 => one hundredth
             101 => one hundred first
             272 => two hundred seventy-second
           23456 => twenty-three thousand four hundred fifty-sixth
8007006005004003 => eight quadrillion seven trillion six billion five million four thousand third
             123 => one hundred twenty-third
         00123.0 => one hundred twenty-third
          1.23e2 => one hundred twenty-third
           1,2,3 => one hundred twenty-third
       0b1111011 => one hundred twenty-third
           0o173 => one hundred twenty-third
            0x7B => one hundred twenty-third
         2706/22 => one hundred twenty-third
      C{ 123 0 } => one hundred twenty-third

FreeBASIC

Dim Shared small(19) As String*9 => { _
"zero", "one", "two", "three", "four", "five", "six",  "seven", "eight", _
"nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", _
"sixteen", "seventeen", "eighteen", "nineteen" }
Dim Shared tens(9) As String*7 => { "", "", _
"twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" }
Dim Shared illions(6) As String*12 => {"", _
" thousand", " million", " billion"," trillion", " quadrillion", " quintillion" }
Dim Shared irregularOrdinals(7, 1) As String*7 = { _
{"one", "first"}, {"two", "second"}, {"three", "third"}, {"five", "fifth"}, _
{"eight", "eighth"}, {"nine", "ninth"}, {"twelve", "twelfth"} }

Function spell(n As Integer) As String    
    Dim As String sx, ix, t = ""
    Dim As Integer s, i, p
    
    If n < 0 Then
        t = "negative "
        n = -n
    End If
    If n < 20 Then
        t &= small(n)
    Elseif n < 100 Then
        t &= tens(n \ 10)
        s = n Mod 10
        If s > 0 Then t &= "-" & small(s)
    Elseif n < 1000 Then
        t &= small(n \ 100) & " hundred"
        s = n Mod 100
        If s > 0 Then t &= " " & spell(s)
    Else
        sx = ""
        i = 0
        While n > 0
            p = n Mod 1000
            n \= 1000
            If p > 0 Then
                ix = spell(p) & illions(i)
                If sx <> "" Then ix &= " " & sx
                sx = ix
            End If
            i += 1
        Wend
        t &= sx
    End If
    Return t
End Function

Function sayOrdinal(n As Integer) As String
    Dim As String s = spell(n)
    Dim As String lastWord = ""
    Dim As Integer j, i = Len(s)
    
    While i > 0 And Mid(s, i, 1) <> " " And Mid(s, i, 1) <> "-"
        lastWord = Mid(s, i, 1) + lastWord
        i -= 1
    Wend
    For j = 0 To Ubound(irregularOrdinals, 1)
        If irregularOrdinals(j, 0) = lastWord Then
            Return Left(s, i) + irregularOrdinals(j, 1)
        End If
    Next j
    If Right(s, 1) = "y" Then
        Return Left(s, Len(s) - 1) + "ieth"
    Else
        Return s + "th"
    End If
End Function

Dim As Integer t(0 To ...) = { 1, 2, 3, 4, 5, 11, 65, 100, 101, 272, _
23456, 8007006005004003, 123, 00123.0, 1.23E2 }
For n As Integer = 0 To Ubound(t)
    Print sayOrdinal(t(n))
Next n

Sleep
Output:
first
second
third
fourth
fifth
eleventh
sixty-fifth
one hundredth
one hundred first
two hundred seventy-second
twenty-three thousand four hundred fifty-sixth
eight quadrillion seven trillion six billion five million four thousand third
one hundred twenty-third
one hundred twenty-third
one hundred twenty-third

Go

As with the Kotlin solution, this uses the output of say from the Number_names task.

import (
	"fmt"
	"strings"
)

func main() {
	for _, n := range []int64{
		1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 8007006005004003,
	} {
		fmt.Println(sayOrdinal(n))
	}
}

var irregularOrdinals = map[string]string{
	"one":    "first",
	"two":    "second",
	"three":  "third",
	"five":   "fifth",
	"eight":  "eighth",
	"nine":   "ninth",
	"twelve": "twelfth",
}

func sayOrdinal(n int64) string {
	s := say(n)
	i := strings.LastIndexAny(s, " -")
	i++
	// Now s[:i] is everything upto and including the space or hyphen
	// and s[i:] is the last word; we modify s[i:] as required.
	// Since LastIndex returns -1 if there was no space/hyphen,
	// `i` will be zero and this will still be fine.
	if x, ok := irregularOrdinals[s[i:]]; ok {
		s = s[:i] + x
	} else if s[len(s)-1] == 'y' {
		s = s[:i] + s[i:len(s)-1] + "ieth"
	} else {
		s = s[:i] + s[i:] + "th"
	}
	return s
}

// Below is a copy of https://rosettacode.org/wiki/Number_names#Go

var small = [...]string{"zero", "one", "two", "three", "four", "five", "six",
	"seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen",
	"fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"}
var tens = [...]string{"", "", "twenty", "thirty", "forty",
	"fifty", "sixty", "seventy", "eighty", "ninety"}
var illions = [...]string{"", " thousand", " million", " billion",
	" trillion", " quadrillion", " quintillion"}

func say(n int64) string {
	var t string
	if n < 0 {
		t = "negative "
		// Note, for math.MinInt64 this leaves n negative.
		n = -n
	}
	switch {
	case n < 20:
		t += small[n]
	case n < 100:
		t += tens[n/10]
		s := n % 10
		if s > 0 {
			t += "-" + small[s]
		}
	case n < 1000:
		t += small[n/100] + " hundred"
		s := n % 100
		if s > 0 {
			t += " " + say(s)
		}
	default:
		// work right-to-left
		sx := ""
		for i := 0; n > 0; i++ {
			p := n % 1000
			n /= 1000
			if p > 0 {
				ix := say(p) + illions[i]
				if sx != "" {
					ix += " " + sx
				}
				sx = ix
			}
		}
		t += sx
	}
	return t
}
Output:
first
second
third
fourth
fifth
eleventh
sixty-fifth
one hundredth
one hundred first
two hundred seventy-second
twenty-three thousand four hundred fifty-sixth
eight quadrillion seven trillion six billion five million four thousand third

Haskell

Uses solution of Number_names#Haskell

spellOrdinal :: Integer -> String
spellOrdinal n
 | n <=   0  = "not ordinal"
 | n <   20  = small n
 | n < 100   = case divMod n 10 of
     (k, 0) -> spellInteger (10*k) ++ "th"
     (k, m) -> spellInteger (10*k) ++ "-" ++ spellOrdinal m
 | n < 1000 = case divMod n 100 of
     (k, 0) -> spellInteger (100*k) ++ "th"
     (k, m) -> spellInteger (100*k) ++ " and " ++ spellOrdinal m
 | otherwise = case divMod n 1000 of
     (k, 0) -> spellInteger (1000*k) ++ "th"
     (k, m) -> spellInteger (k*1000) ++ s ++ spellOrdinal m
       where s = if m < 100 then " and " else ", "   
  where 
   small = ([ undefined, "first", "second", "third", "fourth", "fifth"
            , "sixth", "seventh", "eighth", "nineth", "tenth", "eleventh"
            , "twelveth", "thirteenth", "fourteenth", "fifteenth", "sixteenth"
            , "seventeenth", "eighteenth", "nineteenth"] !!) . fromEnum

Testing

main = mapM_ (\n -> putStrLn $ show n ++ "\t" ++ spellOrdinal n)
  [1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 8007006005004003]
λ> main
1	first
2	second
3	third
4	fourth
5	fifth
11	eleventh
65	sixty-fifth
100	one hundredth
101	one hundred and first
272	two hundred and seventy-second
23456	twenty-three thousand, four hundred and fifty-sixth
8007006005004003	eight quadrillion, seven trillion, six billion, five million, four thousand and third

J

Here, we follow J's best practice for ordinal numbers, which is that 0 is first and 1 is second. This emphasizes the distinction between cardinal numbers like 0 and ordinal numbers like first, accurately represents array indices, and neatly captures a variety of related linguistic issues.

Also, we use definitions from the number names task and the N'th task:

ord=: {{
  ((us,suf)1+y) rplc ;:{{)n onest first twond second 
    threerd third fiveth fifth eightth eighth
}}-.LF
}}

Examples:

   ord 0
first
   ord 1
second
   ord 2
third
   ord 3
fourth
   ord 4
fifth
   ord 5
sixth
   ord 11
twelveth
   ord 65
sixty-sixth
   ord 100
one hundred first
   ord 101
one hundred second
   ord 272
two hundred seventy-third
   ord 23456
twenty-three thousand four hundred fifty-seventh
   ord 8007006005004003
eight quadrillion seven trillion six billion five million four thousand fourth
   ord 123
one hundred twenty-fourth
   ord 00123.0
one hundred twenty-fourth
   ord 1.23e2
one hundred twenty-fourth

Java

import java.util.HashMap;
import java.util.Map;

public class SpellingOfOrdinalNumbers {

    public static void main(String[] args) {
        for ( long test : new long[] {1,  2,  3,  4,  5,  11,  65,  100,  101,  272,  23456,  8007006005004003L} ) {
            System.out.printf("%d = %s%n", test, toOrdinal(test));
        }
    }

    private static Map<String,String> ordinalMap = new HashMap<>();
    static {
        ordinalMap.put("one", "first");
        ordinalMap.put("two", "second");
        ordinalMap.put("three", "third");
        ordinalMap.put("five", "fifth");
        ordinalMap.put("eight", "eighth");
        ordinalMap.put("nine", "ninth");
        ordinalMap.put("twelve", "twelfth");
    }
    
    private static String toOrdinal(long n) {
        String spelling = numToString(n);
        String[] split = spelling.split(" ");
        String last = split[split.length - 1];
        String replace = "";
        if ( last.contains("-") ) {
            String[] lastSplit = last.split("-");
            String lastWithDash = lastSplit[1];
            String lastReplace = "";
            if ( ordinalMap.containsKey(lastWithDash) ) {
                lastReplace = ordinalMap.get(lastWithDash);
            }
            else if ( lastWithDash.endsWith("y") ) {
                lastReplace = lastWithDash.substring(0, lastWithDash.length() - 1) + "ieth";
            }
            else {
                lastReplace = lastWithDash + "th";
            }
            replace = lastSplit[0] + "-" + lastReplace;
        }
        else {
            if ( ordinalMap.containsKey(last) ) {
                replace = ordinalMap.get(last);
            }
            else if ( last.endsWith("y") ) {
                replace = last.substring(0, last.length() - 1) + "ieth";
            }
            else {
                replace = last + "th";
            }
        }
        split[split.length - 1] = replace;
        return String.join(" ", split);
    }

    private static final String[] nums = new String[] {
            "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", 
            "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
    };
    
    private static final String[] tens = new String[] {"zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};

    private static final String numToString(long n) {
        return numToStringHelper(n);
    }
    
    private static final String numToStringHelper(long n) {
        if ( n < 0 ) {
            return "negative " + numToStringHelper(-n);
        }
        int index = (int) n;
        if ( n <= 19 ) {
            return nums[index];
        }
        if ( n <= 99 ) {
            return tens[index/10] + (n % 10 > 0 ? "-" + numToStringHelper(n % 10) : "");
        }
        String label = null;
        long factor = 0;
        if ( n <= 999 ) {
            label = "hundred";
            factor = 100;
        }
        else if ( n <= 999999) {
            label = "thousand";
            factor = 1000;
        }
        else if ( n <= 999999999) {
            label = "million";
            factor = 1000000;
        }
        else if ( n <= 999999999999L) {
            label = "billion";
            factor = 1000000000;
        }
        else if ( n <= 999999999999999L) {
            label = "trillion";
            factor = 1000000000000L;
        }
        else if ( n <= 999999999999999999L) {
            label = "quadrillion";
            factor = 1000000000000000L;
        }
        else {
            label = "quintillion";
            factor = 1000000000000000000L;
        }
        return numToStringHelper(n / factor) + " " + label + (n % factor > 0 ? " " + numToStringHelper(n % factor ) : "");
    }

}
Output:
1 = first
2 = second
3 = third
4 = fourth
5 = fifth
11 = eleventh
65 = sixty-fifth
100 = one hundredth
101 = one hundred first
272 = two hundred seventy-second
23456 = twenty-three thousand four hundred fifty-sixth
8007006005004003 = eight quadrillion seven trillion six billion five million four thousand third

jq

Adapted from Wren

Works with: jq

Also works with gojq and fq, the Go implementations

One point of interest in the following is that the program checks not only that its integer input is within the scope of the program, but also that it is within the capability of the C or Go platform. In particular, `check_ok` checks for the platform's precision of integer arithmetic.

For further remarks on this point, see the comments that are included in the test data.

# integer division for precision when using gojq
def idivide($j):
  . as $i
  | ($i % $j) as $mod
  | ($i - $mod) / $j ;

def lpad($len): tostring | ($len - length) as $l | (" " * $l)[:$l] + .;

def small:
  ["zero", "one", "two", "three", "four", "five", "six",  "seven", "eight", "nine", "ten",
   "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"];

def tens:
  ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"];

# Up to 1e63 (vigintillion)
def illions:
  ["", " thousand", " million", " billion"," trillion", " quadrillion", " quintillion",
       " sextillion", " septillion", " octillion", " nonillion", " decillion", " undecillion",
       " duodecillion", " tredecillion", " quattuordecillion", " quindecillion",
       " sexdecillion", " septendecillion", " octadecillion", " novemdecillion",
       " vigintillion"
];

def illions($i):
  if $i >= (illions|length) then "\($i * 3) zeros is beyond the scope of this exercise" | error
  else illions[$i]
  end;

def irregularOrdinals: {
    "one":    "first",
    "two":    "second",
    "three":  "third",
    "five":   "fifth",
    "eight":  "eighth",
    "nine":   "ninth",
    "twelve": "twelfth"
};

def check_ok:
  def ieee754:
    9007199254740993==9007199254740992;
  if ieee754
  then if (. > 0 and (. + 1) == .) or  (. < 0 and (. - 1) == .)
       then  "The number \(.) is too large for this platform" | error
       else .
       end
  else .
  end;
  
# error courtesy of illions/1 if order of magnitude is too large
def say:
  check_ok
  | { t: "", n: .}
  | if .n < 0 then .t = "negative " | .n *= -1 else . end
  | if .n < 20
    then .t += small[.n]
    elif .n < 100
    then .t += tens[.n | idivide(10)]
    | .s = .n % 10
    | if .s > 0 then .t +=  "-" + small[.s] else . end
    elif .n < 1000
    then .t += small[.n | idivide(100)] + " hundred"
    | .s = .n % 100
    | if .s > 0 then .t +=  " " + (.s|say) else . end
    else .sx = ""
    | .i = 0
    | until (.n <= 0;
          .p = .n % 1000
          | .n = (.n | idivide(1000))
          | if .p > 0
            then .ix = (.p|say) + illions(.i)
            | if .sx != "" then .ix += " " + .sx else . end
            | .sx = .ix
            else .
            end
          | .i += 1 )
    | .t += .sx
    end
    | .t ;

def sayOrdinal:
  {n: ., s: say}
  | .r = (.s | explode | reverse | implode)
  | .i1 = (.r|index(" "))
  | if .i1 then .i1 = (.s|length) - 1 - .i1 else . end
  | .i2 = (.r|index("-"))
  | if .i2 then .i2 = (.s|length) - 1 - .i2 else . end
  # Set .i to 0 iff there was no space or hyphen:
  | .i = (if .i1 or .i2 then 1 + ([.i1,.i2] | max) else 0 end)
  # Now s[.i:] is the last word:
  | irregularOrdinals[.s[.i:]] as $x
  | if $x then .s[:.i] + $x
    elif .s | endswith("y")
    then .s[:-1] + "ieth"
    else .s + "th"
    end;

# Sample inputs
(1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456,
 8007006005004003,
 9007199254740991,
 2e12,
 9007199254740993,   # too large for jq
  
 # 1e63 is vigintillion so gojq should be able to handle 999 * 1e63
 999000000000000000000000000000000000000000000000000000000000000000,
  
 # ... but not 1000 vigintillion
 1000000000000000000000000000000000000000000000000000000000000000000
 )
| "\(lpad(10)) => \(sayOrdinal)"
Output:

The output using gojq is shown first. The tail of the output using jq is then shown to illustrate what happens when the program determines the given integer is too large for jq's built-in support for integer arithmetic.

Using gojq

         1 => first
         2 => second
         3 => third
         4 => fourth
         5 => fifth
        11 => eleventh
        65 => sixty-fifth
       100 => one hundredth
       101 => one hundred first
       272 => two hundred seventy-second
     23456 => twenty-three thousand four hundred fifty-sixth
8007006005004003 => eight quadrillion seven trillion six billion five million four thousand third
9007199254740991 => nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-first
2000000000000 => two trillionth
9007199254740993 => nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-third
999000000000000000000000000000000000000000000000000000000000000000 => nine hundred ninety-nine vigintillionth
gojq: error: 66 zeros is beyond the scope of this exercise

Tail of output using the C implementation

2000000000000 => two trillionth
jq: error (at <unknown>): The number 9007199254740992 is too large for this platform

Julia

This makes use of code posted on this site by MichaeLeroy for a similar task at Number_names#Julia. The function num2text is used (but not included here) as posted from that location.

const irregular = Dict("one" => "first", "two" => "second", "three" => "third", "five" => "fifth", 
                                "eight" => "eighth", "nine" => "ninth", "twelve" => "twelfth")
const suffix = "th"
const ysuffix = "ieth"

function numtext2ordinal(s)
    lastword = split(s)[end]
    redolast = split(lastword, "-")[end]
    if redolast != lastword
        lastsplit = "-"
        word = redolast
    else
        lastsplit = " "
        word = lastword
    end
    firstpart = reverse(split(reverse(s), lastsplit, limit=2)[end])
    firstpart = (firstpart == word) ? "": firstpart * lastsplit
    if haskey(irregular, word)
        word = irregular[word]
    elseif word[end] == 'y'
        word = word[1:end-1] * ysuffix
    else
        word = word * suffix
    end
    firstpart * word
end

const testcases =  [1  2  3  4  5  11  65  100  101  272  23456  8007006005004003]
for n in testcases
    println("$n => $(numtext2ordinal(num2text(n)))")
end
Output:
1 => first
2 => second
3 => third
4 => fourth
5 => fifth
11 => eleventh
65 => sixty-fifth
100 => one hundredth
101 => one hundred and first
272 => two hundred and seventy-second
23456 => twenty-three thousand four hundred and fifty-sixth
8007006005004003 => eight quadrillion seven trillion six billion five million four thousand and third

Kotlin

This makes use of the code at https://rosettacode.org/wiki/Number_names#Kotlin, which I also wrote, and adjusts it to output the corresponding ordinal. Although, for good measure, the program can deal with negative integers, zero and UK-style numbers (the insertion of 'and' at strategic places, no 'milliards' I promise!) none of these are actually tested in line with the task's requirements.

// version 1.1.4-3

typealias IAE = IllegalArgumentException

val names = mapOf(
    1 to "one",
    2 to "two",
    3 to "three",
    4 to "four",
    5 to "five",
    6 to "six",
    7 to "seven",
    8 to "eight",
    9 to "nine",
    10 to "ten",
    11 to "eleven",
    12 to "twelve",
    13 to "thirteen",
    14 to "fourteen",
    15 to "fifteen",
    16 to "sixteen",
    17 to "seventeen",
    18 to "eighteen",
    19 to "nineteen",
    20 to "twenty",
    30 to "thirty",
    40 to "forty",
    50 to "fifty",
    60 to "sixty",
    70 to "seventy",
    80 to "eighty",
    90 to "ninety"
)
val bigNames = mapOf(
    1_000L to "thousand",
    1_000_000L to "million",
    1_000_000_000L to "billion",
    1_000_000_000_000L to "trillion",
    1_000_000_000_000_000L to "quadrillion",
    1_000_000_000_000_000_000L to "quintillion"
)

val irregOrdinals = mapOf(
    "one" to "first",
    "two" to "second",
    "three" to "third",
    "five" to "fifth",
    "eight" to "eighth",
    "nine" to "ninth",
    "twelve" to "twelfth"
)

fun String.toOrdinal(): String {
    val splits = this.split(' ', '-')
    var last = splits[splits.lastIndex]
    return if (irregOrdinals.containsKey(last)) this.dropLast(last.length) + irregOrdinals[last]!!
           else if (last.endsWith("y")) this.dropLast(1) + "ieth"
           else this + "th"
} 
 
fun numToOrdinalText(n: Long, uk: Boolean = false): String {
    if (n == 0L) return "zeroth"  // or alternatively 'zeroeth'
    val neg = n < 0L
    val maxNeg = n == Long.MIN_VALUE
    var nn = if (maxNeg) -(n + 1) else if (neg) -n else n
    val digits3 = IntArray(7)
    for (i in 0..6) {  // split number into groups of 3 digits from the right
        digits3[i] = (nn % 1000).toInt()
        nn /= 1000
    }
 
    fun threeDigitsToText(number: Int) : String {
        val sb = StringBuilder()
        if (number == 0) return ""
        val hundreds = number / 100
        val remainder = number % 100
        if (hundreds > 0) {
            sb.append(names[hundreds], " hundred")
            if (remainder > 0) sb.append(if (uk) " and " else " ")
        }
        if (remainder > 0) {
            val tens = remainder / 10
            val units = remainder % 10
            if (tens > 1) {
                sb.append(names[tens * 10])
                if (units > 0) sb.append("-", names[units])
            }
            else sb.append(names[remainder])
        }
        return sb.toString()
    }
 
    val strings = Array<String>(7) { threeDigitsToText(digits3[it]) }
    var text = strings[0]
    var andNeeded = uk && digits3[0] in 1..99
    var big = 1000L
    for (i in 1..6) {
        if (digits3[i] > 0) {
            var text2 = strings[i] + " " + bigNames[big]
            if (text.length > 0) {
                text2 += if (andNeeded) " and " else ", "
                andNeeded = false
            }
            else andNeeded = uk && digits3[i] in 1..99
            text = text2 + text
        }
        big *= 1000
    }
    if (maxNeg) text = text.dropLast(5) + "eight"
    if (neg) text = "minus " + text
    return text.toOrdinal()
}

fun numToOrdinalText(s: String, uk: Boolean = false): String {
    val d = s.toDoubleOrNull() ?: throw IAE("String is not numeric") 
    if (d !in Long.MIN_VALUE.toDouble() .. Long.MAX_VALUE.toDouble())
        throw IAE("Double is outside the range of a Long Integer")
    val n = d.toLong()
    if (n.toDouble() != d) throw IAE("String does not represent a Long Integer")
    return numToOrdinalText(n, uk)
}
 
fun main(args: Array<String>) {
    val la = longArrayOf(1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 8007006005004003)
    println("Using US representation:")
    for (i in la) println("${"%16d".format(i)} = ${numToOrdinalText(i)}")
    val sa = arrayOf("123", "00123.0", "1.23e2")
    for (s in sa) println("${"%16s".format(s)} = ${numToOrdinalText(s)}")
}
Output:
Using US representation:
               1 = first
               2 = second
               3 = third
               4 = fourth
               5 = fifth
              11 = eleventh
              65 = sixty-fifth
             100 = one hundredth
             101 = one hundred first
             272 = two hundred seventy-second
           23456 = twenty-three thousand, four hundred fifty-sixth
8007006005004003 = eight quadrillion, seven trillion, six billion, five million, four thousand, third
             123 = one hundred twenty-third
         00123.0 = one hundred twenty-third
          1.23e2 = one hundred twenty-third

Nim

Translation of: Python

As in the Python solution, we reuse the output of spellInteger from the Number_names task.

import strutils, algorithm, tables

const irregularOrdinals = {"one":    "first",
                           "two":    "second",
                           "three":  "third",
                           "five":   "fifth",
                           "eight":  "eighth",
                           "nine":   "ninth",
                           "twelve": "twelfth"}.toTable

const
  tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy",
          "eighty", "ninety"]
  small = ["zero", "one", "two", "three", "four", "five", "six", "seven",
           "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
           "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"]
  huge = ["", "", "million", "billion", "trillion", "quadrillion",
          "quintillion", "sextillion", "septillion", "octillion", "nonillion",
          "decillion"]

# Forward reference.
proc spellInteger(n: int64): string

proc nonzero(c: string; n: int64; connect = ""): string =
  if n == 0: "" else: connect & c & spellInteger(n)

proc lastAnd(num: string): string =
  if ',' in num:
    let pos =  num.rfind(',')
    var (pre, last) = if pos >= 0: (num[0 ..< pos], num[pos+1 .. num.high])
                      else: ("", num)
    if " and " notin last: last = " and" & last
    result = [pre, ",", last].join()
  else:
    result = num

proc big(e, n: int64): string =
  if e == 0:
    spellInteger(n)
  elif e == 1:
    spellInteger(n) & " thousand"
  else:
    spellInteger(n) & " " & huge[e]

iterator base1000Rev(n: int64): int64 =
  var n = n
  while n != 0:
    let r = n mod 1000
    n = n div 1000
    yield r

proc spellInteger(n: int64): string =
  if n < 0:
    "minus " & spellInteger(-n)
  elif n < 20:
    small[int(n)]
  elif n < 100:
    let a = n div 10
    let b = n mod 10
    tens[int(a)] & nonzero("-", b)
  elif n < 1000:
    let a = n div 100
    let b = n mod 100
    small[int(a)] & " hundred" & nonzero(" ", b, " and")
  else:
    var sq = newSeq[string]()
    var e = 0
    for x in base1000Rev(n):
      if x > 0: sq.add big(e, x)
      inc e
    reverse sq
    lastAnd(sq.join(", "))

proc num2ordinal(n: SomeInteger|SomeFloat): string =

  let n = n.int64

  var num = spellInteger(n)
  let hyphen = num.rsplit('-', 1)
  var number = num.rsplit(' ', 1)
  var delim = ' '
  if number[^1].len > hyphen[^1].len:
    number = hyphen
    delim = '-'

  if number[^1] in irregularOrdinals:
    number[^1] = delim & irregularOrdinals[number[^1]]
  elif number[^1].endswith('y'):
    number[^1] = delim & number[^1][0..^2] & "ieth"
  else:
    number[^1] = delim & number[^1] & "th"

  result = number.join()


when isMainModule:

  const
    tests1 = [int64 1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 8007006005004003, 123]
    tests2 = [0123.0, 1.23e2]

  for num in tests1:
    echo "$1 => $2".format(num, num2ordinal(num))
  for num in tests2:
    echo  "$1 => $2".format(num, num2ordinal(num))
Output:
1 =>  first
2 =>  second
3 =>  third
4 =>  fourth
5 =>  fifth
11 =>  eleventh
65 => sixty-fifth
100 => one hundredth
101 => one hundred and first
272 => two hundred and seventy-second
23456 => twenty-three thousand, four hundred and fifty-sixth
8007006005004003 => eight quadrillion, seven trillion, six billion, five million, four thousand, and third
123 => one hundred and twenty-third
123.0 => one hundred and twenty-third
123.0 => one hundred and twenty-third

Perl

Adding zero to the input forces a numeric conversion (any identity operation would suffice).

use Lingua::EN::Numbers 'num2en_ordinal';

printf "%16s : %s\n", $_, num2en_ordinal(0+$_) for
    <1 2 3 4 5 11 65 100 101 272 23456 8007006005004003 123 00123.0 '00123.0' 1.23e2 '1.23e2'>;
Output:
               1 : first
               2 : second
               3 : third
               4 : fourth
               5 : fifth
              11 : eleventh
              65 : sixty-fifth
             100 : one hundredth
             101 : one hundred and first
             272 : two hundred and seventy-second
           23456 : twenty-three thousand four hundred and fifty-sixth
8007006005004003 : eight quadrillion, seven trillion, six billion, five million, four thousand and third
             123 : one hundred and twenty-third
         00123.0 : one hundred and twenty-third
         00123.0 : one hundred and twenty-third
          1.23e2 : one hundred and twenty-third
          1.23e2 : one hundred and twenty-third

Phix

Standard builtin

constant tests = {1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 8007006005004003,
                  123, 00123.0, 1.23e2, 0b1111011, 0o173, 0x7B, 861/7}

for i=1 to length(tests) do
    puts(1,ordinal(tests[i])&'\n')
end for
Output:
first
second
third
fourth
fifth
eleventh
sixty-fifth
one hundredth
one hundred and first
two hundred and seventy-second
twenty-three thousand, four hundred and fifty-sixth
eight quadrillion, seven trillion, six billion, five million, four thousand, and third
one hundred and twenty-third
one hundred and twenty-third
one hundred and twenty-third
one hundred and twenty-third
one hundred and twenty-third
one hundred and twenty-third
one hundred and twenty-third

Prolog

Works with: SWI Prolog
test_ordinal(Number):-
    number_name(Number, ordinal, Name),
    writef('%w: %w\n', [Number, Name]).
    
main:-
    test_ordinal(1),
    test_ordinal(2),
    test_ordinal(3),
    test_ordinal(4),
    test_ordinal(5),
    test_ordinal(11),
    test_ordinal(15),
    test_ordinal(21),
    test_ordinal(42),
    test_ordinal(65),
    test_ordinal(98),
    test_ordinal(100),
    test_ordinal(101),
    test_ordinal(272),
    test_ordinal(300),
    test_ordinal(750),
    test_ordinal(23456),
    test_ordinal(7891233),
    test_ordinal(8007006005004003).

Module for spelling numbers in US English:

:- module(number_name, [number_name/3]).

small_name(0, zero, zeroth).
small_name(1, one, first).
small_name(2, two, second).
small_name(3, three, third).
small_name(4, four, fourth).
small_name(5, five, fifth).
small_name(6, six, sixth).
small_name(7, seven, seventh).
small_name(8, eight, eighth).
small_name(9, nine, ninth).
small_name(10, ten, tenth).
small_name(11, eleven, eleventh).
small_name(12, tewlve, twelfth).
small_name(13, thirteen, thirteenth).
small_name(14, fourteen, fourteenth).
small_name(15, fifteen, fifteenth).
small_name(16, sixteen, sixteenth).
small_name(17, seventeen, seventeenth).
small_name(18, eighteen, eighteenth).
small_name(19, nineteen, nineteenth).
small_name(20, twenty, twentieth).
small_name(30, thirty, thirtieth).
small_name(40, forty, fortieth).
small_name(50, fifty, fiftieth).
small_name(60, sixty, sixtieth).
small_name(70, seventy, seventieth).
small_name(80, eighty, eightieth).
small_name(90, ninety, ninetieth).

big_names([big(100, hundred, hundredth),
    big(1000, thousand, thousandth),
    big(1000000, million, millionth),
    big(1000000000, billion, billionth),
    big(1000000000000, trillion, trillionth),
    big(1000000000000000, quadrillion, quadrillionth),
    big(1000000000000000000, quintillion, quintillionth),
    big(1000000000000000000000, sextillion, sextillionth),
    big(1000000000000000000000000, septillion, septillionth)]).

big_name(Number, Big):-
    big_names(Names),
    big_name(Names, Number, Big).

big_name([Big], _, Big):-
    !.
big_name([Big1, Big2|_], Number, Big1):-
    Big2 = big(N2, _, _),
    Number < N2,
    !.
big_name([_|Names], Number, Big):-
    big_name(Names, Number, Big).

get_big_name(big(_, C, _), cardinal, C).
get_big_name(big(_, _, O), ordinal, O).

get_small_name(Number, cardinal, Name):-
    small_name(Number, Name, _),
    !.
get_small_name(Number, ordinal, Name):-
    small_name(Number, _, Name).

number_name(Number, Type, Name):-
    Number < 20,
    !,
    get_small_name(Number, Type, Name).
number_name(Number, Type, Name):-
    Number < 100,
    !,
    N is Number mod 10,
    (N = 0 ->
        get_small_name(Number, Type, Name)
        ;
        N1 is Number - N,
        get_small_name(N1, cardinal, Name1),
        get_small_name(N, Type, Name2),
        atomic_list_concat([Name1, '-', Name2], Name)
     ).
number_name(Number, Type, Name):-
    big_name(Number, big(P, C, O)),
    N is Number // P,
    number_name(N, cardinal, Name1),
    M is Number mod P,
    (M = 0 ->
        get_big_name(big(P, C, O), Type, Name2)
        ;
        number_name(M, Type, Name3),
        atomic_list_concat([C, ' ', Name3], Name2)
    ),
    atomic_list_concat([Name1, ' ', Name2], Name).
Output:
1: first
2: second
3: third
4: fourth
5: fifth
11: eleventh
15: fifteenth
21: twenty-first
42: forty-second
65: sixty-fifth
98: ninety-eighth
100: one hundredth
101: one hundred first
272: two hundred seventy-second
300: three hundredth
750: seven hundred fiftieth
23456: twenty-three thousand four hundred fifty-sixth
7891233: seven million eight hundred ninety-one thousand two hundred thirty-third
8007006005004003: eight quadrillion seven trillion six billion five million four thousand third

Python

As with the Go solution, this uses the output of spell_integer from the Number_names task.

irregularOrdinals = {
	"one":    "first",
	"two":    "second",
	"three":  "third",
	"five":   "fifth",
	"eight":  "eighth",
	"nine":   "ninth",
	"twelve": "twelfth",
}

def num2ordinal(n):
    conversion = int(float(n))
    num = spell_integer(conversion)
    hyphen = num.rsplit("-", 1)
    num = num.rsplit(" ", 1)
    delim = " "
    if len(num[-1]) > len(hyphen[-1]):
        num = hyphen
        delim = "-"
    
    if num[-1] in irregularOrdinals:
        num[-1] = delim + irregularOrdinals[num[-1]]
    elif num[-1].endswith("y"):
        num[-1] = delim + num[-1][:-1] + "ieth"
    else:
        num[-1] = delim + num[-1] + "th"
    return "".join(num)
    
if __name__ == "__main__":
    tests = "1  2  3  4  5  11  65  100  101  272  23456  8007006005004003 123   00123.0   1.23e2".split()
    for num in tests:
        print("{} => {}".format(num, num2ordinal(num)))


#This is a copy of the code from https://rosettacode.org/wiki/Number_names#Python

TENS = [None, None, "twenty", "thirty", "forty",
        "fifty", "sixty", "seventy", "eighty", "ninety"]
SMALL = ["zero", "one", "two", "three", "four", "five",
         "six", "seven", "eight", "nine", "ten", "eleven",
         "twelve", "thirteen", "fourteen", "fifteen",
         "sixteen", "seventeen", "eighteen", "nineteen"]
HUGE = [None, None] + [h + "illion" 
                       for h in ("m", "b", "tr", "quadr", "quint", "sext", 
                                  "sept", "oct", "non", "dec")]
 
def nonzero(c, n, connect=''):
    return "" if n == 0 else connect + c + spell_integer(n)
 
def last_and(num):
    if ',' in num:
        pre, last = num.rsplit(',', 1)
        if ' and ' not in last:
            last = ' and' + last
        num = ''.join([pre, ',', last])
    return num
 
def big(e, n):
    if e == 0:
        return spell_integer(n)
    elif e == 1:
        return spell_integer(n) + " thousand"
    else:
        return spell_integer(n) + " " + HUGE[e]
 
def base1000_rev(n):
    # generates the value of the digits of n in base 1000
    # (i.e. 3-digit chunks), in reverse.
    while n != 0:
        n, r = divmod(n, 1000)
        yield r
 
def spell_integer(n):
    if n < 0:
        return "minus " + spell_integer(-n)
    elif n < 20:
        return SMALL[n]
    elif n < 100:
        a, b = divmod(n, 10)
        return TENS[a] + nonzero("-", b)
    elif n < 1000:
        a, b = divmod(n, 100)
        return SMALL[a] + " hundred" + nonzero(" ", b, ' and')
    else:
        num = ", ".join([big(e, x) for e, x in
                         enumerate(base1000_rev(n)) if x][::-1])
        return last_and(num)

Output

1 =>  first
2 =>  second
3 =>  third
4 =>  fourth
5 =>  fifth
11 =>  eleventh
65 => sixty-fifth
100 => one hundredth
101 => one hundred and first
272 => two hundred and seventy-second
23456 => twenty-three thousand, four hundred and fifty-sixth
8007006005004003 => eight quadrillion, seven trillion, six billion, five million, four thousand, and third
123 => one hundred and twenty-third
00123.0 => one hundred and twenty-third
1.23e2 => one hundred and twenty-third

Quackery

name$ is defined at Number names#Quackery.

switch, case, and otherwise are defined at Metaprogramming#Quackery.

  [ name$
    dup -2 split nip
    [ switch
      $ "ne" case
        [ -3 split drop
          $ "first" join ]
      $ "wo" case
        [ -3 split drop
          $ "second" join ]
      $ "ee" case
        [ -3 split drop
          $ "ird" join ]
      $ "ve" case
        [ -2 split drop
          $ "fth" join ]
      $ "ht" case
        [ $ "h" join ]
      $ "ty" case
        [ -1 split drop
          $ "ieth" join ]
      otherwise
        [ $ "th" join ] ] ] is nameth$ ( n --> $ )

  ' [ 1 2 3 4 5 11 65 100 101 272 23456 8007006005004003 ]
  witheach
    [ dup echo say " = " nameth$ echo$ cr ]
Output:
1 = first
2 = second
3 = third
4 = fourth
5 = fifth
11 = eleventh
65 = sixty fifth
100 = one hundredth
101 = one hundred and first
272 = two hundred and seventy second
23456 = twenty three thousand and four hundred and fifty sixth
8007006005004003 = eight quadrillion, seven trillion, six billion, five million, four thousand and third

Raku

(formerly Perl 6)

Works with: Rakudo version 2019.07.1

Rakudo version 2019.07.1 is updated to Unicode version 12.1. Unicode version 12.0 introduced some new numeric digits, which changed the output here a bit. This will work with earlier versions of Rakudo, but will yield slightly different results.

This would be pretty simple to implement from scratch; it would be straightforward to do a minor modification of the Number names task code. Much simpler to just use the Lingua::EN::Numbers module from the Raku ecosystem though. It will easily handles ordinal number conversions.

We need to be slightly careful of terminology. In Raku, 123, 00123.0, & 1.23e2 are not all integers. They are respectively an Int (integer), a Rat (rational number) and a Num (floating point number). (The fourth numeric is a Complex) For this task it doesn't much matter as the ordinal routine coerces its argument to an Int, but to Raku they are different things. We can further abuse allomorphic and String types and role mixins for some somewhat non-intuitive results as well.

Note that the different allomorphic integer forms of 123 are evaluated on use, not on assignment. They can be passed around in parameters, but until they are used numerically, they retain their stringy characteristics and are distinctive, determinable through introspection. The numerics are evaluated on assignment, hence the stringified output not exactly matching the input format. The mixin role returns different things depending on what context you evaluate it under. When evaluated as a string it is 17, numerically, it is 123.

Raku uses Unicode natively. If a glyph has a Unicode 'Numeric Digit' (<:Nd>) property, it is treated as a numeric digit, and may be used as one.

It is not really clear what is meant by "Write a driver and a function...". Well, the function part is clear enough; driver not so much. Perhaps this will suffice.

use Lingua::EN::Numbers;

# The task
+$_ ?? printf( "Type: \%-14s %16s : %s\n", .^name, $_, .&ordinal ) !! say "\n$_:" for

# Testing
'Required tests',
1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 8007006005004003,

'Optional tests - different forms of 123',
'Numerics',
123, 00123.0, 1.23e2, 123+0i,

'Allomorphs',
|<123 1_2_3 00123.0 1.23e2 123+0i 0b1111011 0o173 0x7B 861/7>,

'Numeric Strings',
|'1_2_3 00123.0 1.23e2 123+0i 0b1111011 0o173 0x7B 861/7'.words,

'Unicode Numeric Strings',
# (Only using groups of digits from the same Unicode block. Technically,
# digits from any block could be combined with digits from any other block.)
|(^0x1FFFF).grep( { .chr ~~ /<:Nd>/ and .unival == 1|2|3 }).rotor(3)».chr».join,

'Role Mixin',
'17' but 123;
Output:
Required tests:
Type: Int                           1 : first
Type: Int                           2 : second
Type: Int                           3 : third
Type: Int                           4 : fourth
Type: Int                           5 : fifth
Type: Int                          11 : eleventh
Type: Int                          65 : sixty-fifth
Type: Int                         100 : one hundredth
Type: Int                         101 : one hundred first
Type: Int                         272 : two hundred seventy-second
Type: Int                       23456 : twenty-three thousand, four hundred fifty-sixth
Type: Int            8007006005004003 : eight quadrillion, seven trillion, six billion, five million, four thousand third

Optional tests - different forms of 123:

Numerics:
Type: Int                         123 : one hundred twenty-third
Type: Rat                         123 : one hundred twenty-third
Type: Num                         123 : one hundred twenty-third
Type: Complex                  123+0i : one hundred twenty-third

Allomorphs:
Type: IntStr                      123 : one hundred twenty-third
Type: IntStr                    1_2_3 : one hundred twenty-third
Type: RatStr                  00123.0 : one hundred twenty-third
Type: NumStr                   1.23e2 : one hundred twenty-third
Type: ComplexStr               123+0i : one hundred twenty-third
Type: IntStr                0b1111011 : one hundred twenty-third
Type: IntStr                    0o173 : one hundred twenty-third
Type: IntStr                     0x7B : one hundred twenty-third
Type: RatStr                    861/7 : one hundred twenty-third

Numeric Strings:
Type: Str                       1_2_3 : one hundred twenty-third
Type: Str                     00123.0 : one hundred twenty-third
Type: Str                      1.23e2 : one hundred twenty-third
Type: Str                      123+0i : one hundred twenty-third
Type: Str                   0b1111011 : one hundred twenty-third
Type: Str                       0o173 : one hundred twenty-third
Type: Str                        0x7B : one hundred twenty-third
Type: Str                       861/7 : one hundred twenty-third

Unicode Numeric Strings:
Type: Str                         123 : one hundred twenty-third
Type: Str                         ١٢٣ : one hundred twenty-third
Type: Str                         ۱۲۳ : one hundred twenty-third
Type: Str                         ߁߂߃ : one hundred twenty-third
Type: Str                         १२३ : one hundred twenty-third
Type: Str                         ১২৩ : one hundred twenty-third
Type: Str                         ੧੨੩ : one hundred twenty-third
Type: Str                         ૧૨૩ : one hundred twenty-third
Type: Str                         ୧୨୩ : one hundred twenty-third
Type: Str                         ௧௨௩ : one hundred twenty-third
Type: Str                         ౧౨౩ : one hundred twenty-third
Type: Str                         ೧೨೩ : one hundred twenty-third
Type: Str                         ൧൨൩ : one hundred twenty-third
Type: Str                         ෧෨෩ : one hundred twenty-third
Type: Str                         ๑๒๓ : one hundred twenty-third
Type: Str                         ໑໒໓ : one hundred twenty-third
Type: Str                         ༡༢༣ : one hundred twenty-third
Type: Str                         ၁၂၃ : one hundred twenty-third
Type: Str                         ႑႒႓ : one hundred twenty-third
Type: Str                         ១២៣ : one hundred twenty-third
Type: Str                         ᠑᠒᠓ : one hundred twenty-third
Type: Str                         ᥇᥈᥉ : one hundred twenty-third
Type: Str                         ᧑᧒᧓ : one hundred twenty-third
Type: Str                         ᪁᪂᪃ : one hundred twenty-third
Type: Str                         ᪑᪒᪓ : one hundred twenty-third
Type: Str                         ᭑᭒᭓ : one hundred twenty-third
Type: Str                         ᮱᮲᮳ : one hundred twenty-third
Type: Str                         ᱁᱂᱃ : one hundred twenty-third
Type: Str                         ᱑᱒᱓ : one hundred twenty-third
Type: Str                         ꘡꘢꘣ : one hundred twenty-third
Type: Str                         ꣑꣒꣓ : one hundred twenty-third
Type: Str                         ꤁꤂꤃ : one hundred twenty-third
Type: Str                         ꧑꧒꧓ : one hundred twenty-third
Type: Str                         ꧱꧲꧳ : one hundred twenty-third
Type: Str                         ꩑꩒꩓ : one hundred twenty-third
Type: Str                         ꯱꯲꯳ : one hundred twenty-third
Type: Str                         123 : one hundred twenty-third
Type: Str                         𐒡𐒢𐒣 : one hundred twenty-third
Type: Str                         𐴱𐴲𐴳 : one hundred twenty-third
Type: Str                         𑁧𑁨𑁩 : one hundred twenty-third
Type: Str                         𑃱𑃲𑃳 : one hundred twenty-third
Type: Str                         𑄷𑄸𑄹 : one hundred twenty-third
Type: Str                         𑇑𑇒𑇓 : one hundred twenty-third
Type: Str                         𑋱𑋲𑋳 : one hundred twenty-third
Type: Str                         𑑑𑑒𑑓 : one hundred twenty-third
Type: Str                         𑓑𑓒𑓓 : one hundred twenty-third
Type: Str                         𑙑𑙒𑙓 : one hundred twenty-third
Type: Str                         𑛁𑛂𑛃 : one hundred twenty-third
Type: Str                         𑜱𑜲𑜳 : one hundred twenty-third
Type: Str                         𑣡𑣢𑣣 : one hundred twenty-third
Type: Str                         𑱑𑱒𑱓 : one hundred twenty-third
Type: Str                         𑵑𑵒𑵓 : one hundred twenty-third
Type: Str                         𑶡𑶢𑶣 : one hundred twenty-third
Type: Str                         𖩡𖩢𖩣 : one hundred twenty-third
Type: Str                         𖭑𖭒𖭓 : one hundred twenty-third
Type: Str                         𝟏𝟐𝟑 : one hundred twenty-third
Type: Str                         𝟙𝟚𝟛 : one hundred twenty-third
Type: Str                         𝟣𝟤𝟥 : one hundred twenty-third
Type: Str                         𝟭𝟮𝟯 : one hundred twenty-third
Type: Str                         𝟷𝟸𝟹 : one hundred twenty-third
Type: Str                         𞥑𞥒𞥓 : one hundred twenty-third

Role Mixin:
Type: Str+{<anon|1>}               17 : one hundred twenty-third

REXX

/*REXX programs spells out  ordinal numbers  (in English, using the American system).   */
numeric digits 3000                              /*just in case the user uses gihugic #s*/
parse arg n                                      /*obtain optional arguments from the CL*/

if n='' | n=","  then n= 1  2  3  4  5  11  65  100  101  272  23456  8007006005004003

pgmOpts= 'ordinal  quiet'                        /*define options needed for $SPELL#.REX*/


     do j=1  for words(n)                        /*process each of the specified numbers*/
     x=word(n, j)                                /*obtain a number from the input list. */
     os=$spell#(x  pgmOpts)                      /*invoke REXX routine to spell ordinal#*/
     say right(x, max(20, length(x) ) )      ' spelled ordinal number ───► '      os
     end   /*j*/
output   when using the default inputs:
                   1  spelled ordinal number ───►  first
                   2  spelled ordinal number ───►  second
                   3  spelled ordinal number ───►  third
                   4  spelled ordinal number ───►  fourth
                   5  spelled ordinal number ───►  fifth
                  11  spelled ordinal number ───►  eleventh
                  65  spelled ordinal number ───►  sixty-fifth
                 100  spelled ordinal number ───►  one hundredth
                 101  spelled ordinal number ───►  one hundred first
                 272  spelled ordinal number ───►  two hundred seventy-second
               23456  spelled ordinal number ───►  twenty-three thousand four hundred fifty-sixth
    8007006005004003  spelled ordinal number ───►  eight quadrillion seven trillion six billion five million four thousand third

The   $SPELL#.REX   routine can be found here   ───►   $SPELL#.REX.

Rust

Translation of: C++
struct NumberNames {
    cardinal: &'static str,
    ordinal: &'static str,
}

impl NumberNames {
    fn get_name(&self, ordinal: bool) -> &'static str {
        if ordinal {
            return self.ordinal;
        }
        self.cardinal
    }
}

const SMALL_NAMES: [NumberNames; 20] = [
    NumberNames {
        cardinal: "zero",
        ordinal: "zeroth",
    },
    NumberNames {
        cardinal: "one",
        ordinal: "first",
    },
    NumberNames {
        cardinal: "two",
        ordinal: "second",
    },
    NumberNames {
        cardinal: "three",
        ordinal: "third",
    },
    NumberNames {
        cardinal: "four",
        ordinal: "fourth",
    },
    NumberNames {
        cardinal: "five",
        ordinal: "fifth",
    },
    NumberNames {
        cardinal: "six",
        ordinal: "sixth",
    },
    NumberNames {
        cardinal: "seven",
        ordinal: "seventh",
    },
    NumberNames {
        cardinal: "eight",
        ordinal: "eighth",
    },
    NumberNames {
        cardinal: "nine",
        ordinal: "ninth",
    },
    NumberNames {
        cardinal: "ten",
        ordinal: "tenth",
    },
    NumberNames {
        cardinal: "eleven",
        ordinal: "eleventh",
    },
    NumberNames {
        cardinal: "twelve",
        ordinal: "twelfth",
    },
    NumberNames {
        cardinal: "thirteen",
        ordinal: "thirteenth",
    },
    NumberNames {
        cardinal: "fourteen",
        ordinal: "fourteenth",
    },
    NumberNames {
        cardinal: "fifteen",
        ordinal: "fifteenth",
    },
    NumberNames {
        cardinal: "sixteen",
        ordinal: "sixteenth",
    },
    NumberNames {
        cardinal: "seventeen",
        ordinal: "seventeenth",
    },
    NumberNames {
        cardinal: "eighteen",
        ordinal: "eighteenth",
    },
    NumberNames {
        cardinal: "nineteen",
        ordinal: "nineteenth",
    },
];

const TENS: [NumberNames; 8] = [
    NumberNames {
        cardinal: "twenty",
        ordinal: "twentieth",
    },
    NumberNames {
        cardinal: "thirty",
        ordinal: "thirtieth",
    },
    NumberNames {
        cardinal: "forty",
        ordinal: "fortieth",
    },
    NumberNames {
        cardinal: "fifty",
        ordinal: "fiftieth",
    },
    NumberNames {
        cardinal: "sixty",
        ordinal: "sixtieth",
    },
    NumberNames {
        cardinal: "seventy",
        ordinal: "seventieth",
    },
    NumberNames {
        cardinal: "eighty",
        ordinal: "eightieth",
    },
    NumberNames {
        cardinal: "ninety",
        ordinal: "ninetieth",
    },
];

struct NamedNumber {
    cardinal: &'static str,
    ordinal: &'static str,
    number: usize,
}

impl NamedNumber {
    fn get_name(&self, ordinal: bool) -> &'static str {
        if ordinal {
            return self.ordinal;
        }
        self.cardinal
    }
}

const N: usize = 7;
const NAMED_NUMBERS: [NamedNumber; N] = [
    NamedNumber {
        cardinal: "hundred",
        ordinal: "hundredth",
        number: 100,
    },
    NamedNumber {
        cardinal: "thousand",
        ordinal: "thousandth",
        number: 1000,
    },
    NamedNumber {
        cardinal: "million",
        ordinal: "millionth",
        number: 1000000,
    },
    NamedNumber {
        cardinal: "billion",
        ordinal: "billionth",
        number: 1000000000,
    },
    NamedNumber {
        cardinal: "trillion",
        ordinal: "trillionth",
        number: 1000000000000,
    },
    NamedNumber {
        cardinal: "quadrillion",
        ordinal: "quadrillionth",
        number: 1000000000000000,
    },
    NamedNumber {
        cardinal: "quintillion",
        ordinal: "quintillionth",
        number: 1000000000000000000,
    },
];

fn big_name(n: usize) -> &'static NamedNumber {
    for i in 1..N {
        if n < NAMED_NUMBERS[i].number {
            return &NAMED_NUMBERS[i - 1];
        }
    }
    &NAMED_NUMBERS[N - 1]
}

fn number_name(n: usize, ordinal: bool) -> String {
    if n < 20 {
        return String::from(SMALL_NAMES[n].get_name(ordinal));
    } else if n < 100 {
        if n % 10 == 0 {
            return String::from(TENS[n / 10 - 2].get_name(ordinal));
        }
        let s1 = TENS[n / 10 - 2].get_name(false);
        let s2 = SMALL_NAMES[n % 10].get_name(ordinal);
        return format!("{}-{}", s1, s2);
    }
    let big = big_name(n);
    let mut result = number_name(n / big.number, false);
    result.push(' ');
    if n % big.number == 0 {
        result.push_str(big.get_name(ordinal));
    } else {
        result.push_str(big.get_name(false));
        result.push(' ');
        result.push_str(&number_name(n % big.number, ordinal));
    }
    result
}

fn test_ordinal(n: usize) {
    println!("{}: {}", n, number_name(n, true));
}

fn main() {
    test_ordinal(1);
    test_ordinal(2);
    test_ordinal(3);
    test_ordinal(4);
    test_ordinal(5);
    test_ordinal(11);
    test_ordinal(15);
    test_ordinal(21);
    test_ordinal(42);
    test_ordinal(65);
    test_ordinal(98);
    test_ordinal(100);
    test_ordinal(101);
    test_ordinal(272);
    test_ordinal(300);
    test_ordinal(750);
    test_ordinal(23456);
    test_ordinal(7891233);
    test_ordinal(8007006005004003);
}
Output:
1: first
2: second
3: third
4: fourth
5: fifth
11: eleventh
15: fifteenth
21: twenty-first
42: forty-second
65: sixty-fifth
98: ninety-eighth
100: one hundredth
101: one hundred first
272: two hundred seventy-second
300: three hundredth
750: seven hundred fiftieth
23456: twenty-three thousand four hundred fifty-sixth
7891233: seven million eight hundred ninety-one thousand two hundred thirty-third
8007006005004003: eight quadrillion seven trillion six billion five million four thousand third

Sidef

var lingua_en = frequire('Lingua::EN::Numbers')
var tests = [1,2,3,4,5,11,65,100,101,272,23456,8007006005004003]

tests.each {|n|
    printf("%16s : %s\n", n, lingua_en.num2en_ordinal(n))
}
Output:
               1 : first
               2 : second
               3 : third
               4 : fourth
               5 : fifth
              11 : eleventh
              65 : sixty-fifth
             100 : one hundredth
             101 : one hundred and first
             272 : two hundred and seventy-second
           23456 : twenty-three thousand four hundred and fifty-sixth
8007006005004003 : eight quadrillion, seven trillion, six billion, five million, four thousand and third

Swift

fileprivate class NumberNames {
    let cardinal: String
    let ordinal: String

    init(cardinal: String, ordinal: String) {
        self.cardinal = cardinal
        self.ordinal = ordinal
    }
    
    func getName(_ ordinal: Bool) -> String {
        return ordinal ? self.ordinal : self.cardinal
    }
    
    class func numberName(number: Int, ordinal: Bool) -> String {
        guard number < 100 else {
            return ""
        }
        if number < 20 {
            return smallNames[number].getName(ordinal)
        }
        if number % 10 == 0 {
            return tens[number/10 - 2].getName(ordinal)
        }
        var result = tens[number/10 - 2].getName(false)
        result += "-"
        result += smallNames[number % 10].getName(ordinal)
        return result
    }
    
    static let smallNames = [
        NumberNames(cardinal: "zero", ordinal: "zeroth"),
        NumberNames(cardinal: "one", ordinal: "first"),
        NumberNames(cardinal: "two", ordinal: "second"),
        NumberNames(cardinal: "three", ordinal: "third"),
        NumberNames(cardinal: "four", ordinal: "fourth"),
        NumberNames(cardinal: "five", ordinal: "fifth"),
        NumberNames(cardinal: "six", ordinal: "sixth"),
        NumberNames(cardinal: "seven", ordinal: "seventh"),
        NumberNames(cardinal: "eight", ordinal: "eighth"),
        NumberNames(cardinal: "nine", ordinal: "ninth"),
        NumberNames(cardinal: "ten", ordinal: "tenth"),
        NumberNames(cardinal: "eleven", ordinal: "eleventh"),
        NumberNames(cardinal: "twelve", ordinal: "twelfth"),
        NumberNames(cardinal: "thirteen", ordinal: "thirteenth"),
        NumberNames(cardinal: "fourteen", ordinal: "fourteenth"),
        NumberNames(cardinal: "fifteen", ordinal: "fifteenth"),
        NumberNames(cardinal: "sixteen", ordinal: "sixteenth"),
        NumberNames(cardinal: "seventeen", ordinal: "seventeenth"),
        NumberNames(cardinal: "eighteen", ordinal: "eighteenth"),
        NumberNames(cardinal: "nineteen", ordinal: "nineteenth")
    ]

    static let tens = [
        NumberNames(cardinal: "twenty", ordinal: "twentieth"),
        NumberNames(cardinal: "thirty", ordinal: "thirtieth"),
        NumberNames(cardinal: "forty", ordinal: "fortieth"),
        NumberNames(cardinal: "fifty", ordinal: "fiftieth"),
        NumberNames(cardinal: "sixty", ordinal: "sixtieth"),
        NumberNames(cardinal: "seventy", ordinal: "seventieth"),
        NumberNames(cardinal: "eighty", ordinal: "eightieth"),
        NumberNames(cardinal: "ninety", ordinal: "ninetieth")
    ]
}

fileprivate class NamedPower {
    let cardinal: String
    let ordinal: String
    let number: UInt64
    
    init(cardinal: String, ordinal: String, number: UInt64) {
        self.cardinal = cardinal
        self.ordinal = ordinal
        self.number = number
    }
    
    func getName(_ ordinal: Bool) -> String {
        return ordinal ? self.ordinal : self.cardinal
    }

    class func getNamedPower(_ number: UInt64) -> NamedPower {
        for i in 1..<namedPowers.count {
            if number < namedPowers[i].number {
                return namedPowers[i - 1]
            }
        }
        return namedPowers[namedPowers.count - 1]
    }

    static let namedPowers = [
        NamedPower(cardinal: "hundred", ordinal: "hundredth",
                   number: 100),
        NamedPower(cardinal: "thousand", ordinal: "thousandth",
                   number: 1000),
        NamedPower(cardinal: "million", ordinal: "millionth",
                   number: 1000000),
        NamedPower(cardinal: "billion", ordinal: "billionth",
                   number: 1000000000),
        NamedPower(cardinal: "trillion", ordinal: "trillionth",
                   number: 1000000000000),
        NamedPower(cardinal: "quadrillion", ordinal: "quadrillionth",
                   number: 1000000000000000),
        NamedPower(cardinal: "quintillion", ordinal: "quintillionth",
                   number: 1000000000000000000)
    ]
}

public func numberName(number: UInt64, ordinal: Bool) -> String {
    if number < 100 {
        return NumberNames.numberName(number: Int(truncatingIfNeeded: number),
                                      ordinal: ordinal)
    }
    let p = NamedPower.getNamedPower(number)
    var result = numberName(number: number/p.number, ordinal: false)
    result += " "
    if number % p.number == 0 {
        result += p.getName(ordinal)
    } else {
        result += p.getName(false)
        result += " "
        result += numberName(number: number % p.number, ordinal: ordinal)
    }
    return result
}

func printOrdinal(_ number: UInt64) {
    print("\(number): \(numberName(number: number, ordinal: true))")
}

printOrdinal(1)
printOrdinal(2)
printOrdinal(3)
printOrdinal(4)
printOrdinal(5)
printOrdinal(11)
printOrdinal(15)
printOrdinal(21)
printOrdinal(42)
printOrdinal(65)
printOrdinal(98)
printOrdinal(100)
printOrdinal(101)
printOrdinal(272)
printOrdinal(300)
printOrdinal(750)
printOrdinal(23456)
printOrdinal(7891233)
printOrdinal(8007006005004003)
Output:
1: first
2: second
3: third
4: fourth
5: fifth
11: eleventh
15: fifteenth
21: twenty-first
42: forty-second
65: sixty-fifth
98: ninety-eighth
100: one hundredth
101: one hundred first
272: two hundred seventy-second
300: three hundredth
750: seven hundred fiftieth
23456: twenty-three thousand four hundred fifty-sixth
7891233: seven million eight hundred ninety-one thousand two hundred thirty-third
8007006005004003: eight quadrillion seven trillion six billion five million four thousand third

VBA

Inspired by the Phix solution. Uses Number names

Private Function ordinal(s As String) As String
    Dim irregs As New Collection
    irregs.Add "first", "one"
    irregs.Add "second", "two"
    irregs.Add "third", "three"
    irregs.Add "fifth", "five"
    irregs.Add "eighth", "eight"
    irregs.Add "ninth", "nine"
    irregs.Add "twelfth", "twelve"
    Dim i As Integer
    For i = Len(s) To 1 Step -1
        ch = Mid(s, i, 1)
        If ch = " " Or ch = "-" Then Exit For
    Next i
    On Error GoTo 1
    ord = irregs(Right(s, Len(s) - i))
    ordinal = Left(s, i) & ord
    Exit Function
1:
    If Right(s, 1) = "y" Then
        s = Left(s, Len(s) - 1) & "ieth"
    Else
        s = s & "th"
    End If
    ordinal = s
End Function
Public Sub ordinals()
    tests = [{1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 8007006005004003, 123, 00123.0, 1.23E2}]
    init
    For i = 1 To UBound(tests)
        Debug.Print ordinal(spell(tests(i)))
    Next i
End Sub
Output:
first
second
third
fourth
fifth
eleventh
sixty-fifth
one hundredth
one hundred and first
two hundred and seventy-second
twenty-three thousand, four hundred and fifty-sixth
eight quadrillion, seven trillion, six billion, five million, four thousandth
one hundred and twenty-third
one hundred and twenty-third

V (Vlang)

Translation of: go

As with the Kotlin solution, this uses the output of say from the Number_names task.

fn main() {
    for n in [i64(1), 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 8007006005004003,
     ] {
        println(say_ordinal(n))
    }
}
 
fn say_ordinal(n i64) string {
    mut s := say(n)
    mut i := s.last_index('-') or {s.last_index(' ') or {-1}}
    i++
    // Now s[:i] is everything upto and including the space or hyphen
    // and s[i:] is the last word; we modify s[i:] as required.
    // Since LastIndex returns -1 if there was no space/hyphen,
    // `i` will be zero and this will still be fine.
    ok := s[i..] in irregular_ordinals
    x := irregular_ordinals[s[i..]]
    if ok {
        s = s[..i] + x
    } else if s[s.len-1..s.len] == 'y' {
        s = s[..i] + s[i..s.len-1] + "ieth"
    } else {
        s = s[..i] + s[i..] + "th"
    }
    return s
}
 
// Below is a copy of https://rosettacode.org/wiki/Number_names#Go
 
const (
    small = ["zero", "one", "two", "three", "four", "five", "six",
    "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen",
    "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"]
    tens = ["", "", "twenty", "thirty", "forty",
    "fifty", "sixty", "seventy", "eighty", "ninety"]
    illions = ["", " thousand", " million", " billion",
    " trillion", " quadrillion", " quintillion"]
    irregular_ordinals = {
        "one":    "first",
        "two":    "second",
        "three":  "third",
        "five":   "fifth",
        "eight":  "eighth",
        "nine":   "ninth",
        "twelve": "twelfth",
    }
)
 
fn say(nn i64) string {
    mut n := nn
    mut t := ''
    if n < 0 {
        t = "negative "
        // Note, for math.MinInt64 this leaves n negative.
        n = -n
    }
    if n < 20{
        t += small[n]
    } else if n < 100{
        t += tens[n/10]
        s := n % 10
        if s > 0 {
            t += "-" + small[s]
        }
    } else if n < 1000{
        t += small[n/100] + " hundred"
        s := n % 100
        if s > 0 {
            t += " " + say(s)
        }
    } else {
        // work right-to-left
        mut sx := ""
        for i := 0; n > 0; i++ {
            p := n % 1000
            n /= 1000
            if p > 0 {
                mut ix := say(p) + illions[i]
                if sx != "" {
                    ix += " " + sx
                }
                sx = ix
            }
        }
        t += sx
    }
    return t
}
Output:
first
second
third
fourth
fifth
eleventh
sixty-fifth
one hundredth
one hundred first
two hundred seventy-second
twenty-three thousand four hundred fifty-sixth
eight quadrillion seven trillion six billion five million four thousand third

Wren

Translation of: Go

This reuses the say function from the Number names task.

Note that it is not safe to use this script for numbers with an absolute magnitude >= 2^53 as integers cannot be expressed exactly by Wren's Num type beyond that limit.

var small = ["zero", "one", "two", "three", "four", "five", "six",  "seven", "eight", "nine", "ten", "eleven",
             "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"]

var tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]

var illions = ["", " thousand", " million", " billion"," trillion", " quadrillion", " quintillion"]

var irregularOrdinals = {
    "one":    "first",
    "two":    "second",
    "three":  "third",
    "five":   "fifth",
    "eight":  "eighth",
    "nine":   "ninth",
    "twelve": "twelfth"
}

var say
say = Fn.new { |n|
    var t = ""
    if (n < 0) {
        t = "negative "
        n = -n
    }
    if (n < 20) {
        t = t + small[n]
    } else if (n < 100) {
        t = t + tens[(n/10).floor]
        var s = n % 10
        if (s > 0) t = t + "-" + small[s]
    } else if (n < 1000) {
        t = t + small[(n/100).floor] + " hundred"
        var s = n % 100
        System.write("") // guards against VM recursion bug
        if (s > 0) t = t + " " + say.call(s)
    } else {
        var sx = ""
        var i = 0
        while (n > 0) {
            var p = n % 1000
            n = (n/1000).floor
            if (p > 0) {
                System.write("") // guards against VM recursion bug
                var ix = say.call(p) + illions[i]
                if (sx != "") ix = ix + " " + sx
                sx = ix
            }
            i = i + 1
        }
        t = t + sx
    }
    return t
}

var sayOrdinal = Fn.new { |n|
    var s = say.call(n)
    var r = s[-1..0]
    var i1 = r.indexOf(" ")
    if (i1 != -1) i1 = s.count - 1 - i1
    var i2 = r.indexOf("-")
    if (i2 != -1) i2 = s.count - 1 - i2
    var i = (i1 > i2) ? i1 : i2
    i = i + 1
    // Now s[0...i] is everything up to and including the space or hyphen
	// and s[i..-1] is the last word; we modify s[i..-1] as required.
	// Since indexOf returns -1 if there was no space/hyphen,
	// `i` will be zero and this will still be fine.
    var x = irregularOrdinals[s[i..-1]]
    if (x) {
        return s[0...i] + x
    } else if (s[-1] == "y") {
        return s[0...i] + s[i..-2] + "ieth"
    } else {
        return s[0...i] + s[i..-1] + "th"
    }
}

for (n in [1, 2, 3, 4, 5, 11, 65, 100, 101, 272, 23456, 9007199254740991]) {
    System.print(sayOrdinal.call(n))
}
Output:
first
second
third
fourth
fifth
eleventh
sixty-fifth
one hundredth
one hundred first
two hundred seventy-second
twenty-three thousand four hundred fifty-sixth
nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-first

zkl

fcn nth(n,th=True){
   var [const]
     nmsth=T("","first","second","third","fourth","fifth","sixth","seventh","eighth","ninth"),
     nms1=T("","one","two","three","four","five","six","seven","eight","nine"),
     nms10=T("ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen"),
     nms10th=T("tenth","eleventh","twelfth","thirteenth","fourteenth","fifteenth","sixteenth","seventeenth","eighteenth","nineteenth"),
     nms20=T("twenty","thirty","forty","fifty","sixty","seventy","eighty","ninety"),
     nms1000=T("thousand","million","billion","trillion","quadrillion"); // 3,6,9,12,15

   if     (n<0)  String("negative ",nth(-n,th));
   else if(n<10) th and nmsth[n] or nms1[n];
   else if(n<20) th and nms10th[n-10] or nms10[n-10];
   else if(n<10) th and nmsth[n] or nms1[n];
   else if(n<100){
      m,txt := n%10,nms20[n/10-2];
      if(m) String(txt,dash(n%10,"-",th));
      else  String(txt[0,-1],"ieth");
   }
   else if(n<1000) String(nms1[n/100]," hundred",dash(n%100," ",th));
   else{
      n=n.toInt();   // yuck, only here to handle floats, 1.23-->"first"
      ds:=(n.numDigits()-1)/3*3; // 1e3->3, 1e4-->3, 1e5-->3, 1e6-->6, 1e7-->6
      z:=(10).pow(ds);  // 1234-->1000, 12345-->10000
      thou:=ds/3 - 1;	// 1000-->0, 10000-->0, 2,000,000-->1
      nnn,ys := n/z, n%z;
      String((if(nnn<10) nms1[nnn] else nth(nnn,False)),
	  " ",nms1000[thou],
	  if(ys==0) "th" else String(" ",nth(ys,th)));
   }
}
fcn dash(n,d,th){ if(n) String(d,nth(n,th)) else (th and "th" or "") }
testNs:=L(1,2,3,4,5,11,65,100,101,272,23456,8007006005004003,
          123,00123.0,1.23e2,);
foreach n in (testNs){ 
   if(n.isType(Float)) println("%16.2f --> %s".fmt(n,nth(n)));
   else		       println("%16d --> %s".fmt(n,nth(n)));
}
Output:
               1 --> first
               2 --> second
               3 --> third
               4 --> fourth
               5 --> fifth
              11 --> eleventh
              65 --> sixty-fifth
             100 --> one hundredth
             101 --> one hundred first
             272 --> two hundred seventy-second
           23456 --> twenty-three thousand four hundred fifty-sixth
8007006005004003 --> eight quadrillion seven trillion six billion five million four thousand third
             123 --> one hundred twenty-third
          123.00 --> one hundred twenty-third
          123.00 --> one hundred twenty-third