|
created a unit test for this after converting to long use versus int use. your logic is flawed somewhere I think.
90736213576 resulted in ninety billion seven hundred thirty-six million twenty-one three thousand five hundred seventy-six
should of resulted in ninety billion seven hundred thirty-six million two hundred thirteen thousand five hundred seventy-six
bolded the difference
I think if you broke the integer up into the 3 digit groupings and parsed those groupings on a smaller scale it would be more foolproof.
Here is an alternative for you.
public static string LongToNumeral(this long number)
{
bool neg = number < 0;
if (neg) number = Math.Abs(number);
Func<long, string> Base = i =>
{
switch (i)
{
case 0: return "zero";
case 1: return "one";
case 2: return "two";
case 3: return "three";
case 4: return "four";
case 5: return "five";
case 6: return "six";
case 7: return "seven";
case 8: return "eight";
case 9: return "nine";
case 10: return "ten";
case 11: return "eleven";
case 12: return "twelve";
case 13: return "thirteen";
case 14: return "fourteen";
case 15: return "fifteen";
case 16: return "sixteen";
case 17: return "seventeen";
case 18: return "eighteen";
case 19: return "nineteen";
case 20: return "twenty";
case 30: return "thirty";
case 40: return "forty";
case 50: return "fifty";
case 60: return "sixty";
case 70: return "seventy";
case 80: return "eighty";
case 90: return "ninety";
case 100: return "hundred";
}
return "";
};
var magnitudes = new string[] {
"",
"thousand",
"million",
"billion",
"trillion",
"quadrillion",
"quintillion",
"sextillion",
"septillion",
"octillion",
"nonillion",
"decillion",
"undecillion",
"duodecillion",
"tredecillion",
"quattuordecillion",
"quindecillion",
"sexdecillion",
"septendecillion",
"octodecillion",
"novemdecillion",
"vigintillion",
"X 10^{0}"
};
Func<int, string> toGroup = num =>
{
string ret = string.Empty;
if (num >= 100)
{
int round = num / 100;
if (num - (round * 100) != 0)
{
ret = Base(round) + " " + Base(100) + " and ";
num = num - (round * 100);
}
else
return Base(round) + " " + Base(100);
}
if (num > 20)
{
int round = num / 10;
if (num - (round * 10) != 0)
return ret + Base(round * 10) + "-" + Base(num - (round * 10));
else
return ret + Base(round * 10);
}
return ret + Base(num);
};
Func<int, string, string> toMagnatude = (mag, num) =>
{
if (num == "000") return string.Empty;
return toGroup(int.Parse(num)) + " " + string.Format(
magnitudes[Math.Min(mag, magnitudes.Length - 1)],
mag * 3);
};
if (number == 0) return "zero";
var sNum = number.ToString();
if (sNum.Length % 3 > 0)
sNum = sNum.PadLeft((3 - sNum.Length % 3) + sNum.Length, '0');
var groups = new List<string>();
for (int i = 0; i < sNum.Length; i = i + 3)
groups.Add(sNum.Substring(i, 3));
groups.Reverse();
var final = string.Empty;
for (int i = 0; i < groups.Count; ++i)
final = toMagnatude(i, groups[i]) + " " + final;
return (neg ? "minus " : "") + final.Replace(" ", " ").Trim();
}
modified 14-May-12 15:47pm.
|
|
|
|
|
Ah, this looks really nice, I appreciate the effort.
I can't look into it for some time though :/ As of this message, I'm heading off to bed and I'll likely be playing the just-released Diablo III all day tomorrow.
I'll catch up with you in a day or two hope you dont mind.
|
|
|
|
|
Mossmyr wrote: I'll likely be playing the just-released Diablo III all day
Ah, that explains it, you're not a developer.
|
|
|
|
|
Not yet, but I will be. Or computer engineer.
In time...
|
|
|
|
|
So I changed all ints to longs and converted 90,736,213,576 (without the commas ofc.)
What I got back was nintey billion seven hundred and thirty-six million two hundred and thirteen thousand five hundred and seventy-six.
Seems pretty right to me, why did you get such a weird result?
Looking into your code now, we'll see what I make of it.
|
|
|
|
|
Mossmyr wrote: why did you get such a weird result?
I didn't.
|
|
|
|
|
Sorry, replied to the wrong person. It was intended for Daniel.
|
|
|
|
|
You might consider using Math.DivRem(...) to calculate the integer division result including the reminder in one go. E.g.
Func<int, string> toGroup = num =>
{
string ret = string.Empty;
if (num >= 100)
{
int round = num / 100;
if (num - (round * 100) != 0)
{
ret = Base(round) + " " + Base(100) + " and ";
num = num - (round * 100);
}
else
return Base(round) + " " + Base(100);
}
if (num > 20)
{
int round = num / 10;
if (num - (round * 10) != 0)
return ret + Base(round * 10) + "-" + Base(num - (round * 10));
else
return ret + Base(round * 10);
}
return ret + Base(num);
};
would turn into
Func<int, string> toGroup = num =>
{
string ret = string.Empty;
if (num >= 100)
{
ret = Base(Math.DivRem(num, 100, out num)) + " " + Base(100);
if (num == 0) return ret;
ret += " and ";
}
if (num > 20)
{
ret += Base(10 * Math.DivRem(num, 10, out num));
if (num == 0) return ret;
ret += "-";
}
return ret + Base(num);
};
Cheers
Andi
|
|
|
|
|
Ran my test cases against your alteration... worked like a charm!
List<Tuple<long, string>> testcases = new List<Tuple<long, string>>
{
new Tuple<long,string>(0, "zero"),
new Tuple<long,string>(1, "one"),
new Tuple<long,string>(2, "two"),
new Tuple<long,string>(3, "three"),
new Tuple<long,string>(4, "four"),
new Tuple<long,string>(5, "five"),
new Tuple<long,string>(6, "six"),
new Tuple<long,string>(7, "seven"),
new Tuple<long,string>(8, "eight"),
new Tuple<long,string>(9, "nine"),
new Tuple<long,string>(10, "ten"),
new Tuple<long,string>(11, "eleven"),
new Tuple<long,string>(12, "twelve"),
new Tuple<long,string>(13, "thirteen"),
new Tuple<long,string>(14, "fourteen"),
new Tuple<long,string>(15, "fifteen"),
new Tuple<long,string>(16, "sixteen"),
new Tuple<long,string>(17, "seventeen"),
new Tuple<long,string>(18, "eighteen"),
new Tuple<long,string>(19, "nineteen"),
new Tuple<long,string>(20, "twenty"),
new Tuple<long,string>(30, "thirty"),
new Tuple<long,string>(40, "forty"),
new Tuple<long,string>(50, "fifty"),
new Tuple<long,string>(60, "sixty"),
new Tuple<long,string>(70, "seventy"),
new Tuple<long,string>(80, "eighty"),
new Tuple<long,string>(90, "ninety"),
new Tuple<long,string>(100, "one hundred"),
new Tuple<long,string>(111, "one hundred and eleven"),
new Tuple<long,string>(1036, "one thousand thirty-six"),
new Tuple<long,string>(1000, "one thousand"),
new Tuple<long,string>(1000512, "one million five hundred and twelve"),
new Tuple<long,string>(90736213576, "ninety billion seven hundred and thirty-six million two hundred and thirteen thousand five hundred and seventy-six"),
new Tuple<long,string>(-91, "minus ninety-one"),
};
[TestMethod()]
public void IntToNumTest2()
{
foreach (var v in testcases)
{
long number = v.Item1;
string expected = v.Item2;
string actual;
actual = Numeral.LongToNumeral(number);
Assert.AreEqual(expected, actual);
}
}
|
|
|
|
|
Glad to hear that! I did dump the code only "from brain to paper"...
Cheers
Andi
|
|
|
|
|
shouldn't be:
new Tuple<long,string>(1036, "one thousand and thirty-six")
...works fascinates me...i can stare it for hours...
|
|
|
|
|
wouldn't that change the base logic?
"and" is inserted between the 100's place and the < 100's place of each individual 3 digit magnitude grouping but not between the 3 digit groupings.
So...
one thousand five hundred and thirty-six
one thousand thirty-six
if you put it between the magnitude groupings you could end up with this
five billion and four hundred and thirty-six million and eighty seven thousand and two hundred and fifty-six
all in all I think there are lots of acceptable variations to how you construct the number.
|
|
|
|
|
So I changed all ints to longs and converted 90,736,213,576 (without the commas ofc.)
What I got back was nintey billion seven hundred and thirty-six million two hundred and thirteen thousand five hundred and seventy-six.
Seems pretty right to me, why did you get such a weird result?
Looking into your code now, we'll see what I make of it.
modified 19-May-12 6:36am.
|
|
|
|
|