Galletto Noyala
Galletto Noyala

Reputation: 11

Convert number with decimals (in currency) to words

I want to convert numbers With decimal (in currency )to word

ex.: 12345.60 --> twelve thousand three hundred forty-five dollars and sixty cents

i got this code from http://www.csharp-tutorials.info/2016/04/convert-numbers-to-words-in-c.html

 public static string NumberToWords(int number)
    {
        if (number == 0)
            return "zero";

        if (number < 0)
            return "minus " + NumberToWords(Math.Abs(number));

        string words = "";

        if ((number / 1000000000) > 0)
        {
            words += NumberToWords(number / 1000000000) + " billion ";
            number %= 1000000000;
        }

        if ((number / 1000000) > 0)
        {
            words += NumberToWords(number / 1000000) + " million ";
            number %= 1000000;
        }

        if ((number / 1000) > 0)
        {
            words += NumberToWords(number / 1000) + " thousand ";
            number %= 1000;
        }

        if ((number / 100) > 0)
        {
            words += NumberToWords(number / 100) + " hundred ";
            number %= 100;
        }

        if (number > 0)
        {
            if (words != "")
                words += " ";

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

            if (number < 20)
                words += unitsMap[number];
            else
            {
                words += tensMap[number / 10];
                if ((number % 10) > 0)
                    words += "-" + unitsMap[number % 10];
            }
        }

        return words;
    }

it work totally fine with integers but if input double.. it shows error

because it only accept int.

I try my best with my knowledge but I cant alter the code to get what i want..

Upvotes: 1

Views: 17044

Answers (6)

Bhanu Pratap
Bhanu Pratap

Reputation: 1761

Samples method Calling

double number = 1.99;

Console.WriteLine(string.Format("Amount: {0}", number));
Console.WriteLine(NumberTowords.NumberToWords(number.ToString(), "rupees", "paisa"));
Console.WriteLine(NumberTowords.NumberToWords(number.ToString(), "dollers", "cent"));

Console.WriteLine(string.Format("========================================="));

number = 99.99;

Console.WriteLine(string.Format("Amount: {0}", number));
Console.WriteLine(NumberTowords.NumberToWords(number.ToString(), "rupees", "paisa"));
Console.WriteLine(NumberTowords.NumberToWords(number.ToString(), "dollers", "cent"));

Class for containes related methods

public static class NumberTowords
{
    public static string NumberToWords(string doubleNumber, string mainAmountType, string decimalAmountType)
    {
        int beforeFloatingPoint = Convert.ToInt32(Convert.ToString(doubleNumber).Split('.')[0]);
        string beforeFloatingPointWord = string.Format("{0} {1}", NumberToWords(beforeFloatingPoint), (beforeFloatingPoint > 1 ? mainAmountType : mainAmountType.TrimEnd(mainAmountType.Last())));
        int afterFloatingPoint = Convert.ToInt32(Convert.ToString(doubleNumber).Contains('.') ? Convert.ToString(doubleNumber).Split('.')[1] : "0");
        string afterFloatingPointWord = string.Format("{0} {1} only.", SmallNumberToWord(afterFloatingPoint, ""), decimalAmountType);
        if (afterFloatingPoint > 0)
        {
            return string.Format("{0} and {1}", beforeFloatingPointWord, afterFloatingPointWord);
        }
        else
        {
            return string.Format("{0} only", beforeFloatingPointWord);
        }
    }

    private static string NumberToWords(int number)
    {
        if (number == 0)
            return "zero";

        if (number < 0)
            return "minus " + NumberToWords(Math.Abs(number));

        var words = "";

        if (number / 1000000000 > 0)
        {
            words += NumberToWords(number / 1000000000) + " billion ";
            number %= 1000000000;
        }

        if (number / 1000000 > 0)
        {
            words += NumberToWords(number / 1000000) + " million ";
            number %= 1000000;
        }

        if (number / 1000 > 0)
        {
            words += NumberToWords(number / 1000) + " thousand ";
            number %= 1000;
        }

        if (number / 100 > 0)
        {
            words += NumberToWords(number / 100) + " hundred ";
            number %= 100;
        }

        words = SmallNumberToWord(number, words);

        return words;
    }

    private static string SmallNumberToWord(int number, string words)
    {
        if (number <= 0) return words;
        if (words != "")
            words += "";

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

        if (number < 20)
            words += unitsMap[number];
        else
        {
            words += tensMap[number / 10];
            if ((number % 10) > 0)
                words += " " + unitsMap[number % 10];
        }
        return words;
    }
}

Sample Results enter image description here

Upvotes: 0

Fayeeg
Fayeeg

Reputation: 21

If the target platform/language is .NET/C#, then you can utilize NumericWordsConversion nuget package and customize as per your requirements. Sample code as follows:

using NumericWordsConversion;
using System;
using System.Collections.Generic;
using System.Linq;

namespace CurrencyConverterCore
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Currency to words");
            decimal amount = 111100000.12M;

            List<CurrencyFormat> formats = new List<CurrencyFormat>();
            formats.Add(new CurrencyFormat { CurrencyCode = "USD", CurrencyName="United States Dollar", Culture=Culture.International, CurrencyUnit = "", SubCurrencyUnit = "cents" });
            formats.Add(new CurrencyFormat { CurrencyCode = "MYR", CurrencyName="Ringgit Malaysia", Culture = Culture.International, CurrencyUnit = "", SubCurrencyUnit = "cents" });
            formats.Add(new CurrencyFormat { CurrencyCode = "SGD", CurrencyName = "Singapore Dollar", Culture = Culture.International, CurrencyUnit = "", SubCurrencyUnit = "cents" });
            formats.Add(new CurrencyFormat { CurrencyCode = "INR", CurrencyName = "Indian Rupee", Culture = Culture.Hindi, CurrencyUnit = "rupee", SubCurrencyUnit = "paisa" });
            formats.Add(new CurrencyFormat { CurrencyCode = "THB", CurrencyName = "Thai Baht", Culture = Culture.International, CurrencyUnit = "", SubCurrencyUnit = "satang" });
            formats.Add(new CurrencyFormat { CurrencyCode = "BDT", CurrencyName = "Bangladesh Taka", Culture = Culture.Hindi, CurrencyUnit = "taka", SubCurrencyUnit = "paisa" });

            CurrencyWordsConverter converter = null;

            string currencyToConvert = "BDT";
            string words = "";
            var format = formats.Where(x => x.CurrencyCode == currencyToConvert).FirstOrDefault();

            if (format != null)
            {
                
                converter = new CurrencyWordsConverter(new CurrencyWordsConversionOptions()
                {
                    Culture = format.Culture,
                    OutputFormat = OutputFormat.English,
                    CurrencyUnitSeparator = "and",
                    CurrencyUnit = format.CurrencyUnit,
                    SubCurrencyUnit = format.SubCurrencyUnit,
                    EndOfWordsMarker = "only"
                });

                words = (format.CurrencyUnit == "" ? (format.CurrencyName + " ") : "") + converter.ToWords(amount);
            }
            else
            {
                converter = new CurrencyWordsConverter();
                words = converter.ToWords(amount);
            }


            

            Console.WriteLine(words);
            Console.ReadKey();

        }

        class CurrencyFormat
        {
            public string CurrencyCode { get; set; }
            public string CurrencyName { get; set; }
            public Culture Culture { get; set; }
            public string CurrencyUnit { get; set; }
            public string SubCurrencyUnit { get; set; }

        }
    }
}

Upvotes: 1

Kalpesh Popat
Kalpesh Popat

Reputation: 1526

As an addon to the above answer, since most of the systems are globalised these days it is important to support multiple currencies and support conversion according to the different way a country recognises decimal groups. I solved it by creating template in a json file for each key currency i wanted to support and then use the code similar to above (without the hardcoding) to read data from template and convert accordingly. eg of template is below, let me know if anyone needs the code.

Json template for USD to words

    {
    "currencyKey": "USD",
    "formatMainUnit": "{0} dollar",
    "formatDecimalUnit": "{0} cent",
    "joinMainAndDecimal": "and",
    "ifNoDecimalUnit": "",
    "formatForMinus": "minus {0}",
    "unitsMap": [ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", 
    "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"],
    "tensMap": [ "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" ],
    "groupMap": [
        {"tenRaiseTo":9, "word":"billion"},
        {"tenRaiseTo":6, "word":"million"},
        {"tenRaiseTo":3, "word":"thousand"},
        {"tenRaiseTo":2, "word":"hundred"}
    ]
}

Json template for INR to words

{
    "currencyKey": "INR",
    "formatMainUnit": "{0} rupee",
    "formatDecimalUnit": "{0} paisa",
    "joinMainAndDecimal": "and",
    "ifNoDecimalUnit": "zero paisa",
    "formatForMinus": "minus {0}",
    "unitsMap": [ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", 
    "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"],
    "tensMap": [ "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" ],
    "groupMap": [
        {"tenRaiseTo":7, "word":"crore"},
        {"tenRaiseTo":5, "word":"lak"},
        {"tenRaiseTo":3, "word":"thousand"},
        {"tenRaiseTo":2, "word":"hundred"}
    ]
}

Json template for Thai Baht to words

{
    "currencyKey": "THB",
    "formatMainUnit": "{0} baht",
    "formatDecimalUnit": "{0} satang",
    "joinMainAndDecimal": "and",
    "ifNoDecimalUnit": "No satang",
    "formatForMinus": "minus {0}",
    "unitsMap": [ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", 
    "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"],
    "tensMap": [ "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" ],
    "groupMap": [
        {"tenRaiseTo":9, "word":"billion"},
        {"tenRaiseTo":6, "word":"million"},
        {"tenRaiseTo":3, "word":"thousand"},
        {"tenRaiseTo":2, "word":"hundred"}
    ]
}

Actual C# code that uses the above template to convert numbers to words. Note: replace the custom framework related code with your own implementation (eg: CacheManager)

using System; using System.Collections.Generic; using System.IO;

namespace BF
{
    public class CurrencyToWords
    {
        private CurrencyTemplate template;
        private const string DEFAULT_TEMPLATE = "Default_To_Words";

        public CurrencyToWords(string tplFile)
        {
            if (string.IsNullOrWhiteSpace(tplFile)) tplFile = DEFAULT_TEMPLATE;

            //read the template
            template = LoadTemplate(tplFile);

            if (template == null && tplFile != DEFAULT_TEMPLATE)
            {
                //load default template
                tplFile = DEFAULT_TEMPLATE;

                template = LoadTemplate(tplFile);
            }

            if (template != null)
            {
                //set to cache
                CacheManager.setCacheObject(tplFile, template, 1000);
            }
        }

        private CurrencyTemplate LoadTemplate(string tplFile)
        {
            CurrencyTemplate result = (CurrencyTemplate)CacheManager.getCachedObject(tplFile);

            if (result == null)
            {
                //load from file
                string filePath = CoreConfig.ConstructConfigCurrencyTplPath(tplFile + ".json");

                if (File.Exists(filePath))
                {
                    string jsonMap = System.IO.File.ReadAllText(filePath);
                    result = new CurrencyTemplate();
                    result = (CurrencyTemplate)CommonConversions.DeserializeJSON(jsonMap, result.GetType());
                }
            }

            return result;
        }

        public string ConvertToWords(decimal value)
        {
            if (template == null) return "";

            decimal workingValue = Math.Abs(value);

            decimal beforeFloatingPoint = Math.Floor(workingValue);
            string beforeFloatingPointWord = string.Format(template.formatMainUnit, ConvertParts(beforeFloatingPoint, 0));

            decimal afterFloatingPoint = Math.Floor((workingValue - beforeFloatingPoint) * 100);
            string afterFloatingPointWord;

            if (afterFloatingPoint == 0)
            {
                afterFloatingPointWord = template.ifNoDecimalUnit;
            }
            else
            {
                afterFloatingPointWord = string.Format(template.formatDecimalUnit, ConvertParts(afterFloatingPoint, 0));
            }

            string result;

            if (string.IsNullOrWhiteSpace(afterFloatingPointWord))
            {
                result = beforeFloatingPointWord;
            }
            else
            {
                result = string.Format("{0} {1} {2}", beforeFloatingPointWord, template.joinMainAndDecimal, afterFloatingPointWord);
            }

            //if negative apply template for representing negative value
            if (value < 0)
            {
                result = string.Format(template.formatForMinus, result);
            }

            return result;
        }

        private string ConvertParts(decimal value, int group)
        {
            var words = "";
            CurrencyGroupMap map;
            decimal groupRange;
            decimal workingValue = Math.Floor(value);

            for ( ; group < template.groupMap.Count; group++)
            {
                map = template.groupMap[group];

                groupRange = (decimal) Math.Pow(10, map.tenRaiseTo);

                if (Math.Floor(workingValue / groupRange) > 0)
                {
                    if (words != "")
                        words += ", ";

                    words += string.Format("{0} {1}", ConvertParts(workingValue / groupRange, group + 1), map.word);
                    workingValue %= groupRange;
                }
            }

            words = ConvertSmallNumbers(workingValue, words);

            return words;
        }

        private string ConvertSmallNumbers(decimal value, string words)
        {
            if (value <= 0) return words;

            if (words != "")
                words += " ";

            int index;

            try
            {
                index = (int)value;
            }
            catch
            {
                index = 0;
            }

            if (index < 20)
                words += template.unitsMap[index];
            else
            {
                words += template.tensMap[index / 10];

                if ((index % 10) > 0)
                    words += "-" + template.unitsMap[index % 10];
            }

            return words;
        }
    }

    public class CurrencyTemplate
    {
        public string currencyKey;
        public string formatMainUnit;
        public string formatDecimalUnit;
        public string joinMainAndDecimal;
        public string ifNoDecimalUnit;
        public string formatForMinus;
        public List<string> unitsMap;
        public List<string> tensMap;
        public List<CurrencyGroupMap> groupMap;
    }

    public class CurrencyGroupMap
    {
        public int tenRaiseTo;
        public string word;
    }
}

Upvotes: 1

Amara Miloudi
Amara Miloudi

Reputation: 140

You can use this Nuget Libray : For example =>

using NumberToEnglishWordConverter;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DigitConverter
{
class Program
{
    static void Main(string[] args)
    {
        var number = 2500;
        var result = new NumberToEnglishWordConverter.NumberToEnglishWordConverter().changeCurrencyToWords(number);
        // the result will be => Two Thousand Five Hundred
    }
}

}

The result should be : 2500 => Two Thousand Five Hundred

Upvotes: 0

Matt Brown
Matt Brown

Reputation: 19

Your mapping arrays helped inspire this result, but I went a different route to handle the places. Works well for me. It is geared for American currency. There are a couple of extension methods used (viz. RemoveDoubleSpaces(), TryIntParse()) that will need to be accounted for, but what they do is pretty obvious.

    public static string ToVerbalCurrency(this double value)
    {
        var valueString = value.ToString("N2");
        var decimalString = valueString.Substring(valueString.LastIndexOf('.') + 1);
        var wholeString = valueString.Substring(0, valueString.LastIndexOf('.'));

        var valueArray = wholeString.Split(',');

        var unitsMap = new[] { "", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
        var tensMap = new[] { "", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
        var placeMap = new[] { "", " thousand ", " million ", " billion ", " trillion " };

        var outList = new List<string>();

        var placeIndex = 0;

        for (int i = valueArray.Length - 1; i >= 0; i--)
        {
            var intValue = valueArray[i].TryIntParse();
            var tensValue = intValue % 100;

            var tensString = string.Empty;
            if (tensValue < unitsMap.Length) tensString = unitsMap[tensValue];
            else tensString = tensMap[(tensValue - tensValue % 10) / 10] + " " + unitsMap[tensValue % 10];

            var fullValue = string.Empty;
            if (intValue >= 100) fullValue = unitsMap[(intValue - intValue % 100) / 100] + " hundred " + tensString + placeMap[placeIndex++];
            else if (intValue != 0) fullValue = tensString + placeMap[placeIndex++];
            else placeIndex++;

            outList.Add(fullValue);
        }

        var intCentsValue = decimalString.TryIntParse();

        var centsString = string.Empty;
        if (intCentsValue < unitsMap.Length) centsString = unitsMap[intCentsValue];
        else centsString = tensMap[(intCentsValue - intCentsValue % 10) / 10] + " " + unitsMap[intCentsValue % 10];

        if (intCentsValue == 0) centsString = "zero";

        var output = string.Empty;
        for (int i = outList.Count - 1; i >= 0; i--) output += outList[i];
        output += " dollars and " + centsString + " cents";

        return output.RemoveDoubleSpaces();
    }

Upvotes: 0

MetaColon
MetaColon

Reputation: 2923

The problem is that you use modulo on doubles, which is (obviously) not allowed. you have to use Math.Floor(number) with the given code for the part before the floating point and number - Math.Floor(number) for the part after the floating point. The rest is actually given in your code example, just add "Dollar" after the part before the floating point and "cents" after the part after the floating point. Your code would look somwhat like that:

    public static string NumberToWords(double doubleNumber)
    {
        var beforeFloatingPoint = (int) Math.Floor(doubleNumber);
        var beforeFloatingPointWord = $"{NumberToWords(beforeFloatingPoint)} dollars";
        var afterFloatingPointWord =
            $"{SmallNumberToWord((int) ((doubleNumber - beforeFloatingPoint) * 100), "")} cents";
        return $"{beforeFloatingPointWord} and {afterFloatingPointWord}";
    }

    private static string NumberToWords(int number)
    {
        if (number == 0)
            return "zero";

        if (number < 0)
            return "minus " + NumberToWords(Math.Abs(number));

        var words = "";

        if (number / 1000000000 > 0)
        {
            words += NumberToWords(number / 1000000000) + " billion ";
            number %= 1000000000;
        }

        if (number / 1000000 > 0)
        {
            words += NumberToWords(number / 1000000) + " million ";
            number %= 1000000;
        }

        if (number / 1000 > 0)
        {
            words += NumberToWords(number / 1000) + " thousand ";
            number %= 1000;
        }

        if (number / 100 > 0)
        {
            words += NumberToWords(number / 100) + " hundred ";
            number %= 100;
        }

        words = SmallNumberToWord(number, words);

        return words;
    }

    private static string SmallNumberToWord(int number, string words)
    {
        if (number <= 0) return words;
        if (words != "")
            words += " ";

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

        if (number < 20)
            words += unitsMap[number];
        else
        {
            words += tensMap[number / 10];
            if ((number % 10) > 0)
                words += "-" + unitsMap[number % 10];
        }
        return words;
    }

Upvotes: 5

Related Questions