using System.Diagnostics; // Trace using System.IO; // File using System.Numerics; // BigInteger namespace Packt.Shared { public static class NumbersToWords { // Single-digit and small number names private static string[] smallNumbers = new string[] { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" }; // Tens number names from twenty upwards private static string[] tens = new string[] { "", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" }; // Scale number names for use during recombination private static string[] scaleNumbers = new string[] { "", "thousand", "million", "billion", "trillion", "quadrillion", "quintillion" }; private static int groups = 7; // i.e. up to quintillion public static string ToWords(this int number) { return ToWords((BigInteger)number); } public static string ToWords(this long number) { return ToWords((BigInteger)number); } public static string ToWords(this BigInteger number) { /* Convert A Number into Words by Richard Carr, published at http://www.blackwasp.co.uk/numbertowords.aspx */ /* Zero Rule. If the value is 0 then the number in words is 'zero' and no other rules apply. */ if (number == 0) { return "zero"; } /* Three Digit Rule. The integer value is split into groups of three digits starting from the right-hand side. Each set of three digits is then processed individually as a number of hundreds, tens and units. Once converted to text, the three-digit groups are recombined with the addition of the relevant scale number (thousand, million, billion). */ // Array to hold the specified number of three-digit groups int[] digitGroups = new int[groups]; // Ensure a positive number to extract from var positive = BigInteger.Abs(number); // Extract the three-digit groups for (int i = 0; i < groups; i++) { digitGroups[i] = (int)(positive % 1000); positive /= 1000; } // write to a text file in the project folder Trace.Listeners.Add(new TextWriterTraceListener( File.AppendText("log.txt"))); // text writer is buffered, so this option calls // Flush() on all listeners after writing Trace.AutoFlush = true; // log array of group numbers for (int x = 0; x < digitGroups.Length; x++) { Trace.WriteLine(string.Format( format: "digitGroups[{0}] = {1}", arg0: x, arg1: digitGroups[x])); } // Convert each three-digit group to words string[] groupTexts = new string[groups]; for (int i = 0; i < groups; i++) { // call a local function (see below) groupTexts[i] = ThreeDigitGroupToWords(digitGroups[i]); } // log array of group texts for (int x = 0; x < groupTexts.Length; x++) { Trace.WriteLine(string.Format( format: "groupTexts[{0}] = {1}", arg0: x, arg1: groupTexts[x])); } /* Recombination Rules. When recombining the translated three-digit groups, each group except the last is followed by a large number name and a comma, unless the group is blank and therefore not included at all. One exception is when the final group does not include any hundreds and there is more than one non-blank group. In this case, the final comma is replaced with 'and'. eg. 'one billion, one million and twelve'. */ // Recombine the three-digit groups string combined = groupTexts[0]; bool appendAnd; // Determine whether an 'and' is needed appendAnd = (digitGroups[0] > 0) && (digitGroups[0] < 100); // Process the remaining groups in turn, smallest to largest for (int i = 1; i < groups; i++) { // Only add non-zero items if (digitGroups[i] != 0) { // Build the string to add as a prefix string prefix = groupTexts[i] + " " + scaleNumbers[i]; if (combined.Length != 0) { prefix += appendAnd ? " and " : ", "; } // Opportunity to add 'and' is ended appendAnd = false; // Add the three-digit group to the combined string combined = prefix + combined; } } // Converts a three-digit group into English words string ThreeDigitGroupToWords(int threeDigits) { // Initialise the return text string groupText = ""; // Determine the hundreds and the remainder int hundreds = threeDigits / 100; int tensUnits = threeDigits % 100; /* Hundreds Rules. If the hundreds portion of a three-digit group is not zero, the number of hundreds is added as a word. If the three-digit group is exactly divisible by one hundred, the text 'hundred' is appended. If not, the text "hundred and" is appended. eg. 'two hundred' or 'one hundred and twelve' */ if (hundreds != 0) { groupText += smallNumbers[hundreds] + " hundred"; if (tensUnits != 0) { groupText += " and "; } } // Determine the tens and units int tens = tensUnits / 10; int units = tensUnits % 10; /* Tens Rules. If the tens section of a three-digit group is two or higher, the appropriate '-ty' word (twenty, thirty, etc.) is added to the text and followed by the name of the third digit (unless the third digit is a zero, which is ignored). If the tens and the units are both zero, no text is added. For any other value, the name of the one or two-digit number is added as a special case. */ if (tens >= 2) { groupText += NumbersToWords.tens[tens]; if (units != 0) { groupText += " " + smallNumbers[units]; } } else if (tensUnits != 0) groupText += smallNumbers[tensUnits]; return groupText; } /* Negative Rule. Negative numbers are always preceded by the text 'negative'. */ if (number < 0) { combined = "negative " + combined; } return combined; } } }