Recursive translation of numbers to words

Tom Gunn 3 Tallied Votes 493 Views Share

This is an example of how to turn numbers into words. 1473 would get translated to "one thousand four hundred seventy three", for example. The class has a simple interface and can handle any value representable by unsigned int. The implementation recursively processes groups of 3 digits using table lookups for the word strings.

Note to cheaters: The code was written so that a teacher can tell if you try to steal it and turn it in for a homework grade.

mvmalderen commented: Incredibly nice! +21
Nick Evan commented: That's just awesome. Took a while to figure out ;) +31
amrith92 commented: Must've taken some thought!! I like it :) +2
#include <iostream>
#include <string>

namespace Daniweb
{
    /// <summary>provides string utilities for the std::string class</summary>
    namespace StdStringUtil
    {
        /// <summary>appends rhs to lhs with sep inbetween</summary>
        /// <remarks>sep is not added if either string is blank</remarks>
        /// <param name=lhs>left hand side of the append</param>
        /// <param name=rhs>right hand side of the append</param>
        /// <param name=sep>optional separator string between lhs and rhs</param>
        inline void Append(std::string& lhs, 
                           std::string const& rhs, 
                           std::string const& sep=" ")
        { 
            lhs += ((!lhs.empty() && !rhs.empty()) ? sep : "") + rhs;
        }

        /// <summary>appends rhs to lhs with sep inbetween</summary>
        /// <remarks>Uses void Append(string&, string const&, string const&)</remarks>
        /// <returns>a copy of the result string after appending</returns>
        inline std::string Append(std::string const& lhs, 
                                  std::string const& rhs, 
                                  std::string const& sep=" ")
        {
            std::string temp = lhs;
            Append(temp, rhs, sep);
            return temp;
        }
    }
}

namespace Daniweb
{
    /// <summary>translates numeric triplets to english words</summary>
    /// <example>163 becomes "one hundred sixty three"</example>
    class TripletTranslator
    {
        static std::string const digits[];
        static std::string const teens[];
        static std::string const duplets[];
        static std::string const prefixes[];

        unsigned _original; // original value passed to the constructor
        unsigned _x;        // intermediate value with zero or more triplets
    public:
        /// <summary>default constructor</summary>
        /// <param name=x>the original value to translate</param>
        explicit TripletTranslator(unsigned x=0): _original(x), _x(x) {}

        /// <summary>resets the original value</summary>
        /// <param name=x>the new original value</param>
        void Reset(unsigned x) {_original = x;}

        /// <summary>translates the original value to english words</summary>
        std::string Translate()
        {
            _x = _original;
            return TranslateImpl(0);
        }
    private:
        /// <summary>recursively translates each triplet</summary>
        /// <param name=prefix>recursive depth for figuring out prefixes</param>
        std::string TranslateImpl(unsigned const prefix);

        /// <summary>
        /// builds a triplet string from the least significant triplet in _x
        /// </summary>
        std::string Next();
    };

    std::string const TripletTranslator::digits[] = 
    {
        "",     "one", "two",   "three", "four", 
        "five", "six", "seven", "eight", "nine"
    };

    std::string const TripletTranslator::teens[] =
    {
        "ten",     "eleven",  "twelve",    "thirteen", "fourteen", 
        "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
    };

    std::string const TripletTranslator::duplets[] =
    {
        "",      "",       "twenty", "thirty",  "forty",  
        "fifty", "sixty",  "seventy", "eighty", "ninety"
    };

    std::string const TripletTranslator::prefixes[] = 
    {
        "", "thousand", "million", "billion"
    };

    std::string TripletTranslator::TranslateImpl(unsigned const prefix)
    {
        using StdStringUtil::Append;

        if (_x != 0)
        {
            std::string next = Next();

            // do not double up prefixes if the triplet is 000
            //     eg. "one million" instead of "one million thousand"
            if (!next.empty()) Append(next, prefixes[prefix]);

            return Append(TranslateImpl(prefix + 1), next);
        }
        else if (prefix == 0)
        {
            // no triplets at 0 depth means a total value of 0 for _x
            return "zero";
        }
        else return ""; // recursive base case
    }

    std::string TripletTranslator::Next()
    {
        using StdStringUtil::Append;

        unsigned triplet = _x % 1000; // get the current triplet

        // digits that are not present will be set to 0
        unsigned ldigit = triplet / 100;      // left most digit -- hundreds place
        unsigned mdigit = triplet % 100 / 10; // middle digit -- tens place
        unsigned rdigit = triplet % 10;       // right most digit -- ones place

        // start to build the triplet string at the hundreds place
        std::string s = (ldigit) ? Append(digits[ldigit], "hundred") : "";

        // 10 is treated as a teen for simpler logic, duplets start at 20
        Append(s, duplets[mdigit]);

        // do not translate teens if a duplet exists
        if (mdigit > 1) Append(s, digits[rdigit]);
        else Append(s, (mdigit) ? teens[rdigit] : digits[rdigit]);

        _x /= 1000; // move to the next triplet

        return s;
    }
}

#define TRIPLET_TRANSLATOR_EXAMPLE 1
#if defined(TRIPLET_TRANSLATOR_EXAMPLE)
#include <iomanip>
#include <limits>

int main()
{
    Daniweb::TripletTranslator translator;

    for (unsigned x = 0; x < std::numeric_limits<unsigned>::max(); ++x)
    {
        translator.Reset(x);
        std::cout << std::left << std::setw(12) << x << translator.Translate();
        std::cin.get();
    }
}
#endif
William Hemsworth 1,339 Posting Virtuoso

Nice well programmed snippet.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.