private static string[] units = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
private static string[] tens = { "", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
private static string[] thousandGroups = { "" };
static string DollarsToWords(decimal value) {
return "Dollar cannot be negative.";
if (decimal.ToDouble(value) >= Math.Pow(1000, thousandGroups.Length))
return "Number too large.";
decimal dollarPart = decimal.Truncate(value);
int centPart = decimal.ToInt32(decimal.Truncate((value - decimal.Truncate(value)) * 100));
return DollarsToWords(dollarPart, centPart);
private static string DollarsToWords(decimal dollarPart, int centPart) {
string centString = CentsToWords(centPart);
return centString == null ? "zero dollars" : centString;
return "one dollar" + (centString == null ? "" : " and " + centString);
if (thousandGroups.Length > 1)
return ExtractThousands(dollarPart, 0).TrimEnd() + " dollars" + (centString == null ? "" : " and " + centString);
return HundredToWord(decimal.ToInt32(dollarPart)) + " dollars" + (centString == null ? "" : " and " + centString);
private static string CentsToWords(int i){
return HundredToWord(i) + " cents";
static string HundredToWord(int number){
throw new ArgumentException();
return tens[number / 10] + (number % 10 > 0 ? " " + HundredToWord(number % 10) : "");
return units[number / 100] + " hundred" + (number % 100 > 0 ? " and " + HundredToWord(number % 100) : "");
throw new ArgumentException();
public static void Main() {
Test(0.12m, "twelve cents");
Test(10.55m, "ten dollars and fifty five cents");
Test(120, "one hundred and twenty cents");
Test(0.00m, "zero dollars");
Test(1.09m, "one dollar and nine cents");
Test(0.001m, "zero dollars");
Test(1.999m, "one dollar and ninety nine cents");
Test(0.20m, "twenty cents");
Test(00.99m, "ninety nine cents");
Test(245, "two hundred and forty five dollars");
Test(999.99m, "nine hundred and ninety nine dollars and ninety nine cents");
Test(100, "one hundred dollars");
Test(101, "one hundred and one dollars");
Test(110, "one hundred and ten dollars");
Test(decimal.MinusOne, "Dollar cannot be negative.");
Test(decimal.MaxValue, "Number too large.");
Test(decimal.MinValue, "Dollar cannot be negative.");
if (thousandGroups.Length > 1) {
Test(1100, "one thousand one hundred dollars");
Test(1000000, "one million dollars");
Test(999999999999.99m, "nine hundred and ninety nine billion nine hundred and ninety nine million nine hundred and ninety nine thousand nine hundred and ninety nine dollars and ninety nine cents");
Test(123456789012.34m, "one hundred and twenty three billion four hundred and fifty six million seven hundred and eighty nine thousand twelve dollars and thirty four cents");
Test(1000000000000, "Number too large.");
static void Test(decimal number, string expected) {
var actual = DollarsToWords(number);
if (actual == expected) {
Console.WriteLine($"{number}: PASS");
Console.WriteLine($"{number}: FAIL");
Console.WriteLine($" Expected: {expected}");
Console.WriteLine($" Actual: {actual}");
private static string ExtractThousands(decimal integral, int group) {
decimal thousandPart = Math.Truncate(integral / 1000);
int hundredPart = decimal.ToInt32(integral % 1000);
return ExtractThousands(thousandPart, group + 1) + GroupedHundredToWord(hundredPart, group);
return GroupedHundredToWord(decimal.ToInt32(integral), group);
static string GroupedHundredToWord(int number, int thousandGroup) {
if (thousandGroup >= thousandGroups.Length)
throw new ArgumentException();
return HundredToWord(number);
return HundredToWord(number) + " " + thousandGroups[thousandGroup] + " ";