OmegaMan

Code Competition I had answered a post Convert to letter format a number value and provided a response that went counter to the logic of the threads up to that point. The two code examples provided to the poster used enums. I posted with a title Enums make me E-Numb and provided a code sample that use no enumerations.

One of the other poster's code sample was .Net but based on a previous process he had written in a non .Net language. He commented that he had run my sample and after 100K operations mine was four second faster. He requested that I add some features to the code and repost so he could retest.

I accepted his request and that is what this is...a place to post the code away from the original post so it does not become an OTP from the original.
ChallengeExamine my code and if you have an alternate or any code suggestions please provide feedback. For those who may find this esoterically interesting, I am sure Mark, owner of the other code, will report his findings.

Code Changes
  • Fixed the plural problems such as 1 dollars .
  • Added dash such as eight-eight.
  • Added more tests and fixed code issues since mine is being compared to a production level code.
  • Made the StringBuilder have a size of 64.
  • Added code to handle for no cents and zero dollars.


static string[] verbage = new string[]
{ "zero", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten", "eleven", "twelve",
"thirteen", "fourteen", "fifteen", "sixteen", "seventeen",
"eighteen", "nineteen", "twenty", "thrity", "forty",
"fifty", "sixty", "seventy", "eighty", "ninety",
"hundred", "thousand"
};

static int[] offset = new int[] { 0, 20, 28, 29, 20 };

public static string ShowMe(string money, bool addDollars)
{
StringBuilder result = new StringBuilder();
int decimalPoint = money.IndexOf('.');
string dollars = (decimalPoint == -1) money : money.Substring(0, decimalPoint);
string cents = (decimalPoint == -1) string.Empty : money.Substring(decimalPoint + 1);
int position = dollars.Length - 1;
int previous = -1;
int value = 0;

if (string.IsNullOrEmpty(dollars))
result.AppendFormat("{0} ", verbage[0]);

for (int index = 0; index < dollars.Length; index++, position--)
{
value = int.Parse(dollars[index].ToString());
if (index -1 >= 0)
previous = int.Parse(dollars[index -1].ToString());

switch (position)
{
case 4 :
if (value != 1)
result.AppendFormat("{0}-", verbage[offset[position] + (value - 2)]);
break;

case 3 :
if (value == 0)
continue;

if (previous == 1)
value += 10;

result.AppendFormat("{0} {1} ", verbage[value], verbage[offset[position]]);
break;

case 2 :
if (value == 0)
continue;

result.AppendFormat("{0} {1} ", verbage[value], verbage[offset[position]]);
break;

case 1 :
if (value == 0)
continue;

if (value != 1)
result.AppendFormat("{0}-", verbage[offset[position] + (value - 2)]);
break;

case 0 :
if (value == 0)
{
if (previous != 1)
continue;
value += 10;
}
else
if ((value == 1) && (previous == 1))
value += 10;

result.AppendFormat("{0} ", verbage[value]);
break;
}

}

if (addDollars)
{
if (result.Length == 0)
result.AppendFormat("{0} ", verbage[0]);

result.AppendFormat("dollar{0} ", (((value == 1) && dollars.Length == 1) string.Empty : "s"));
}

if ((string.IsNullOrEmpty(cents) == true) || ((cents[1] == '0') && (cents[0] == '0')))
{
if (addDollars)
result.AppendFormat("and no cents");

}
else
{
result.AppendFormat("and {0}cent{1}", ShowMe(cents, false), ((cents[1] != '1') && (cents[0] != '1') "s" : string.Empty));
}

return result.ToString();

}



Console Output.01 = zero dollars and one cent
0.01 = zero dollars and one cent
.55 = zero dollars and fifty-five cents
.05 = zero dollars and five cents
0 = zero dollars and no cents
1 = one dollar and no cents
1.00 = one dollar and no cents
1.99 = one dollar and ninety-nine cents
2 = two dollars and no cents
11 = eleven dollars and no cents
22 = twenty-two dollars and no cents
88 = eighty-eight dollars and no cents
95 = ninety-five dollars and no cents
99 = ninety-nine dollars and no cents
300 = three hundred dollars and no cents
320 = three hundred twenty-dollars and no cents
48010 = forty-eight thousand ten dollars and no cents
15034 = fifteen thousand thrity-four dollars and no cents
21000 = twenty-one thousand dollars and no cents
95050 = ninety-five thousand dollars and no cents
15000 = fifteen thousand dollars and no cents
3000.99 = three thousand dollars and ninety-nine cents
3000 = three thousand dollars and no cents
12345.67 = twelve thousand three hundred forty-five dollars and sixty-seven cents
99999.99 = ninety-nine thousand nine hundred ninety-nine dollars and ninety-nine cents



Re: Show and Tell Convert Money value to its Textual Representation

ReneeC

Nice !!!!!!!






Re: Show and Tell Convert Money value to its Textual Representation

Biche

Very Nice !




Re: Show and Tell Convert Money value to its Textual Representation

Waldy Almonte

nice work Partner !!




Re: Show and Tell Convert Money value to its Textual Representation

Mark Benningfield

Hello All.

OmegaMan:

Sorry for the delay, we've been coping with an ice storm hereabouts over the weekend.

Okay, first, here's the code for the test harness:

String input code:

static void Main(string[] args)
{
int range = 25;
int count = 0;
int size = 100000;
TimeSpan avgTime = TimeSpan.Zero;

string[] input = new string[size];
Random rnd = new Random();

for (int x = 0; x < size; x++)
{
decimal num = (decimal)rnd.NextDouble() * x;
input[x] = String.Format("{0:F2}", num);
}
while (count < range)
{
DateTime start = DateTime.Now;

for (int x = 0; x < size; x++)
{
Console.WriteLine(ShowMe(input[x],true));
}

avgTime += (DateTime.Now - start);
count++;
}
long avgTicks = (avgTime.Ticks / range) / size;
Console.WriteLine("Average time/call: {0}", TimeSpan.FromTicks(avgTicks));
Console.ReadLine();
}

Decimal input code:

static void Main(string[] args)
{
int range = 25;
int count = 0;
int size = 100000;
TimeSpan avgTime = TimeSpan.Zero;
PayrollAmount pay = new PayrollAmount();

decimal[] input = new decimal[size];
Random rnd = new Random();

for (int x = 0; x < size; x++)
{
input[x] = (decimal)rnd.NextDouble() * x;
}
while (count < range)
{
DateTime start = DateTime.Now;

for (int x = 0; x < size; x++)
{
Console.WriteLine(pay.ToText(input[x]));
}

avgTime += (DateTime.Now - start);
count++;
}
long avgTicks = (avgTime.Ticks / range) / size;
Console.WriteLine("Average time/call: {0}", TimeSpan.FromTicks(avgTicks));
Console.ReadLine();
}

Now, the assumptions:

First, I used the decimal input test harness for both my code and tonhinbm's code, with the obvious changes for the method call. I wanted to run through tonhinbm's code because it uses literals instead of enums, and I wanted to see what difference, if any, it made.

Second, I instantiated the PayrollAmount object outside of the timed execution to focus solely on the method execution itself. I suppose I could have switched over to a static method for my code, but this seemed to be a case of "six of one, half-dozen of the other."

Third, I used 100,000 for the input set size and tied the content to the loop counter, figuring that this would generally be expected to exercise the full range of cases, with the exception on tonhinbm's code, in that it handles inputs up to 1000 times greater. For this, I decided to just have that code operate over the same input as ours.

Last, my intention is to run the test over 5 or 10 different input sets, to get a better idea of overall performance, not just average speed over the same data. I'll probably go with 5, as that would work out to about 2.5 hours per test, and alas, like most, I don't have the patience for testing that I ought to.

For the convenience of this thread, here is my code again:

class PayrollAmount
{
private enum Teens
{
Ten = 10,
Eleven,
Twelve,
Thirteen,
Fourteen,
Fifteen,
Sixteen,
Seventeen,
Eighteen,
Nineteen
};

private enum Tens
{
Twenty = 20,
Thirty = 30,
Forty = 40,
Fifty = 50,
Sixty = 60,
Seventy = 70,
Eighty = 80,
Ninety = 90
};

private enum Ones
{
Zero,
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine
};


internal string ToText(decimal amount)
{
string hundred = " Hundred";
string thousand = " Thousand";
string dollars = " Dollars";
string pennies = " Cents";
string zero = "Zero";
string number = string.Empty;

if (amount >= 1 && amount < 2)
dollars = " Dollar";

int tenThous = (int)amount / 10000;
amount -= (tenThous * 10000);

int thous = (int)amount / 1000;
amount -= (thous * 1000);

int hundreds = (int)amount / 100;
amount -= (hundreds * 100);

int tens = (int)amount / 10;
amount -= (tens * 10);

int ones = (int)amount;
amount -= (ones);

int tenCents = (int)(amount * 100);
int cents = tenCents % 10;
tenCents /= 10;

if (tenCents == 0 && cents == 1)
pennies = " Cent";

if (tenThous > 0)
{
number += BuildTens(tenThous, thous);
number += thousand;
}
else if (thous > 0)
{
number += BuildSingles(thous, thousand);
}
if (hundreds > 0)
{
if (number != string.Empty)
number += " ";

number += BuildSingles(hundreds, hundred);
number += " ";
}
else if(number != string.Empty)
{
number += " ";
}

if (tens > 0)
{
number += BuildTens(tens, ones);
}
else if (ones > 0)
{
number += BuildSingles(ones, string.Empty);
}
if (number == string.Empty)
number = zero;

number += dollars + " and ";

if (tenCents > 0)
{
number += BuildTens(tenCents, cents);
}
else if (cents > 0)
{
number += BuildSingles(cents, string.Empty);
}

if (tenCents == 0 && cents == 0)
{
number += zero + pennies;
}
else
{
number += pennies;
}
return number;
}

private string BuildTens(int tens, int singles)
{
string result = string.Empty;

if (tens > 1) //between 20 and 90
{
switch (tens)
{
case (int)Ones.Two:
result += Tens.Twenty;
break;

case (int)Ones.Three:
result += Tens.Thirty;
break;

case (int)Ones.Four:
result += Tens.Forty;
break;

case (int)Ones.Five:
result += Tens.Fifty;
break;

case (int)Ones.Six:
result += Tens.Sixty;
break;

case (int)Ones.Seven:
result += Tens.Seventy;
break;

case (int)Ones.Eight:
result += Tens.Eighty;
break;

case (int)Ones.Nine:
result += Tens.Ninety;
break;
}

if (singles > 0) //include any singles
{
switch (singles)
{
case (int)Ones.One:
result += "-" + Ones.One;
break;

case (int)Ones.Two:
result += "-" + Ones.Two;
break;

case (int)Ones.Three:
result += "-" + Ones.Three;
break;

case (int)Ones.Four:
result += "-" + Ones.Four;
break;

case (int)Ones.Five:
result += "-" + Ones.Five;
break;

case (int)Ones.Six:
result += "-" + Ones.Six;
break;

case (int)Ones.Seven:
result += "-" + Ones.Seven;
break;

case (int)Ones.Eight:
result += "-" + Ones.Eight;
break;

case (int)Ones.Nine:
result += "-" + Ones.Nine;
break;
}
}
}
else //between 10 and 19
{
switch (singles)
{
case (int)Ones.Zero:
result += Teens.Ten;
break;

case (int)Ones.One:
result += Teens.Eleven;
break;

case (int)Ones.Two:
result += Teens.Twelve;
break;

case (int)Ones.Three:
result += Teens.Thirteen;
break;

case (int)Ones.Four:
result += Teens.Fourteen;
break;

case (int)Ones.Five:
result += Teens.Fifteen;
break;

case (int)Ones.Six:
result += Teens.Sixteen;
break;

case (int)Ones.Seven:
result += Teens.Seventeen;
break;

case (int)Ones.Eight:
result += Teens.Eighteen;
break;

case (int)Ones.Nine:
result += Teens.Nineteen;
break;
}
}
return result;
}

private string BuildSingles(int singles, string tag)
{
string result = string.Empty;

switch (singles)
{
case (int)Ones.One:
result = Ones.One + tag;
break;

case (int)Ones.Two:
result = Ones.Two + tag;
break;

case (int)Ones.Three:
result = Ones.Three + tag;
break;

case (int)Ones.Four:
result = Ones.Four + tag;
break;

case (int)Ones.Five:
result = Ones.Five + tag;
break;

case (int)Ones.Six:
result = Ones.Six + tag;
break;

case (int)Ones.Seven:
result = Ones.Seven + tag;
break;

case (int)Ones.Eight:
result = Ones.Eight + tag;
break;

case (int)Ones.Nine:
result = Ones.Nine + tag;
break;
}

return result;
}
}

Keeping in mind that I haven't gotten your input on the test harness yet, these are the results after an initial run-through with the test code as posted:

String-parse code: Average time/call: 00:00:00:0004746 seconds.
My Decimal code: Average time/call: 00:00:00:0003967 seconds.
tonhinbm's code: Average time/call: 00:00:00:0003225 seconds.

Observations:

First, this an initial run-through, and will likely change if you find a major whoopsie in the test harness code.

Second, since these results didn't jibe with the result of that first down-and-dirty test I did, I ran your code through a second time, and while I haven't had the chance to verify it yet, there seems to be more variation in the results of your code, leading me to believe that for certain input sets, your code would be appreciably faster. As I say, I intend to loop through the test at least 5 times, for different input sets, but I haven't had time yet.

I also intend to run test batches for smaller input set sizes, for more realistic payroll amounts (well, for me anyway ).

Third, without wider testing across input sets, I can't tell if there is a correlation with the performance on tonhinbm's code and the fact that it uses literals instead of enums. That's something else I want to know about.

So, get back to me about the test code, and I'll burn through some more iterations as I have the time, and thanks for responding.

Oh, and for the record and this thread, I'm not out to start a competition. I'm not a maniac about nanosecond performance. Mostly, I write Windows client apps, and 300 milliseconds is generally fast enough to suit me. It's just that I'm dying to see how well the old dog can still hunt.

Thanks again.






Re: Show and Tell Convert Money value to its Textual Representation

OmegaMan

Hi Mark,

I realized I left something out. In my original post, this thread, I mentioned setting StringBuilder's size to 64 but did not include that in my code sample. So if you could change it to do this


new StringBuilder(64)


I would apreciate it.

As to the tests, since a random number generator is being used, it may be advantageous to run the different methods against the same data for each. Its random, but not necessarily the same for each. Or may do both a random sample and the a static sample.

Oh, and for the record and this thread, I'm not out to start a competition. I'm not a maniac about nanosecond performance. Mostly, I write Windows client apps, and 300 milliseconds is generally fast enough to suit me. It's just that I'm dying to see how well the old dog can still hunt.


Ok, competition is solely my responsibility. <g> I thought maybe someone else had and even different way of doing this which might be interesting.

Thanks,

OmegaMan.








Re: Show and Tell Convert Money value to its Textual Representation

Mark Benningfield

Hello All.

OmegaMan:

I realized I left something out. In my original post, this thread, I mentioned setting StringBuilder's size to 64 but did not include that in my code sample. So if you could change it to do this


new StringBuilder(64)


I would apreciate it.

Got it.

As to the tests, since a random number generator is being used, it may be advantageous to run the different methods against the same data for each. Its random, but not necessarily the same for each. Or may do both a random sample and the a static sample.

Yeah, that's a good point. I think what I'm going to do is go ahead and combine the three sets of code, generate an input set, run all three code listings against that input set, and iterate the whole thing about 5 times. Then, I'll just set it to run while I'm snoozing tonite.

...I thought maybe someone else had and even different way of doing this which might be interesting.

Yes, I see your point. It would be very interesting to see another way of doing this. At any rate, I'll work on putting all of this together this evening and let you know in the morning what happens.






Re: Show and Tell Convert Money value to its Textual Representation

Mark Benningfield

Hello All.

OmegaMan:

I forgot to ask you, could you run your code through a short version of the test harness when you get a chance 10 loops of 100 items ought to do it. I'm getting a few anomolous listings from your code, and I can't seem to find anything wrong with the input set, when I set it to print alongside the output from your code.

I think the dashes and plural/singular might need a bit more attention; either that, or I've garfed up the input somehow. Whenever you have time.






Re: Show and Tell Convert Money value to its Textual Representation

OmegaMan

I will give it a shot tonight. Thanks.





Re: Show and Tell Convert Money value to its Textual Representation

Mark Benningfield

Hello All. (Warning: Long Post)

OmegaMan:

Well, I have managed to make some progress, and learn a few things along the way. As I say, I've never been much concerned with precision performance measurements before now, and it took a little experimentation to discover that DateTime is useless for this sort of thing. So, that's the explanation for the fluctuating results I was getting.

I looked a little bit at the PerformanceCounter classes, and it seemed like overkill for this job, but I was noodling my way through it when I realized that I could just grab the high resolution performance counter out of the kernel.

So, here's the final version of the test harness that I'm using, which does a much better job.

  class Program
  {
   [DllImport("kernel32.dll")]
   public static extern bool QueryPerformanceFrequency(out long freq);
   [DllImport("kernel32.dll")]
   public static extern bool QueryPerformanceCounter(out long count);
   static void Main(string[] args)
   {
     //get the performance counter resolution
     long countsPerTick = 0;
     if (QueryPerformanceFrequency(out countsPerTick))
     {
      countsPerTick /= 10000000; //1 tick = 100 nanoseconds
     }
     else
     {
      Console.WriteLine("High Resolution counter is unavailable.");
      Console.ReadLine();
      return;
     }
     //set up 
     PayrollAmount pay = new PayrollAmount();
     Random rnd = new Random();
     int inputValueMagnitude = 100000; //multiple of 10
     int inputSetCount = 10; //number of sets of a given magnitude
     int inputSetSize = 100;     
     int inputSetReps = 25; //number of reps through a given input set
     int avgSetBase = inputSetReps * inputSetSize;
     int avgMagBase = (int)Math.Log10((double)inputValueMagnitude) + 1;
     int mag = 1;
     long start = 0;
     long stop = 0;
     long span = 0;
     long avgTimeStringParse = 0;
     long avgTimeEnums = 0;
     long avgTimeSpanish = 0;
     //JIT the code
     pay.ToText(1.0M);
     ShowMe("1.00", true);
     CNumeros.representacionLiteral(1.0M);
     using (StreamWriter sw = new StreamWriter(@"c:\output.txt"))
     {
      //get input sets for each magnitude
      while (mag <= inputValueMagnitude)
      {
        long magTimeStringParse = 0;
        long magTimeSpanish = 0;
        long magTimeEnums = 0;
        if (mag != 1)
         sw.WriteLine();
        sw.WriteLine("Input Set Magnitude: {0}", mag);
        //get the specified number of sets
        for (int x = 0; x != inputSetCount; x++)
        {
         //build the input sets of the specified size
         decimal[] decInput = new decimal[inputSetSize];
         string[] strInput = new string[inputSetSize];
         for (int y = 0; y != inputSetSize; y++)
         {
           decInput[ y ] = Decimal.Round((decimal)rnd.NextDouble() * mag, 2);
           strInput[ y ] = String.Format("{0:F2}", decInput[ y ]);
         }
         //time the code String Parse
         long elapsed = 0;
         for (int y = 0; y != inputSetReps; y++)
         {
           if (y == 0)
            sw.WriteLine("\tAverage time/call (Set {0})", x);
           QueryPerformanceCounter(out start);
           for (int z = 0; z != inputSetSize; z++)
           {
            string output = ShowMe(strInput[ z ], true);
           }
           QueryPerformanceCounter(out stop);
           elapsed += (stop - start);
         }
         elapsed /= avgSetBase;
         magTimeStringParse += elapsed;
         sw.WriteLine("\t\tString Parse: {0}", new TimeSpan(elapsed / countsPerTick));
         //Enum code
         elapsed = 0;
         for (int y = 0; y != inputSetReps; y++)
         {
           QueryPerformanceCounter(out start);
           for (int z = 0; z != inputSetSize; z++)
           {
            string output = pay.ToText(decInput[ z ]);
           }
           QueryPerformanceCounter(out stop);
           elapsed += (stop - start);
         }
         elapsed /= avgSetBase;
         magTimeEnums += elapsed;
         sw.WriteLine("\t\tEnum Code: {0}", new TimeSpan(elapsed / countsPerTick));
         //Spanish code
         elapsed = 0;
         for (int y = 0; y != inputSetReps; y++)
         {
           QueryPerformanceCounter(out start);
           for (int z = 0; z != inputSetSize; z++)
           {
            string output = CNumeros.representacionLiteral(decInput[ z ]);
           }
           QueryPerformanceCounter(out stop);
           elapsed += (stop - start);
         }
         elapsed /= avgSetBase;
         magTimeSpanish += elapsed;
         sw.WriteLine("\t\tSpanish Code: {0}", new TimeSpan(elapsed / countsPerTick));
        }//next input set for this magnitude
        sw.WriteLine();
        sw.WriteLine("Average time/call (Magnitude {0})", mag);
        span = (magTimeStringParse / inputSetCount) / countsPerTick;
        sw.WriteLine("\tString Parse: {0}", new TimeSpan(span));
        avgTimeStringParse += span;
        span = (magTimeEnums / inputSetCount) / countsPerTick;
        sw.WriteLine("\tEnum code: {0}", new TimeSpan(span));
        avgTimeEnums += span;
        span = (magTimeSpanish / inputSetCount) / countsPerTick;
        sw.WriteLine("\tSpanish code: {0}", new TimeSpan(span));
        avgTimeSpanish += span;
        mag *= 10;
      }//next order of magnitude
      sw.WriteLine();
      sw.WriteLine("Average overall time/call");
      avgTimeStringParse /= avgMagBase;
      avgTimeEnums /= avgMagBase;
      avgTimeSpanish /= avgMagBase;
      sw.WriteLine("\tString Parse: {0}", new TimeSpan(avgTimeStringParse));
      sw.WriteLine("\tEnum code: {0}", new TimeSpan(avgTimeEnums));
      sw.WriteLine("\tSpanish code: {0}", new TimeSpan(avgTimeSpanish));
     }
   }

With this test, here are the results:

Input Set Magnitude: 1
 Average time/call (Set 0)
 String Parse: 00:00:00.0000074
 Enum Code: 00:00:00.0000235
 Spanish Code: 00:00:00.0000056
 Average time/call (Set 1)
 String Parse: 00:00:00.0000037
 Enum Code: 00:00:00.0000215
 Spanish Code: 00:00:00.0000048
 Average time/call (Set 2)
 String Parse: 00:00:00.0000036
 Enum Code: 00:00:00.0000218
 Spanish Code: 00:00:00.0000047
 Average time/call (Set 3)
 String Parse: 00:00:00.0000036
 Enum Code: 00:00:00.0000217
 Spanish Code: 00:00:00.0000047
 Average time/call (Set 4)
 String Parse: 00:00:00.0000037
 Enum Code: 00:00:00.0000241
 Spanish Code: 00:00:00.0000047
 Average time/call (Set 5)
 String Parse: 00:00:00.0000036
 Enum Code: 00:00:00.0000220
 Spanish Code: 00:00:00.0000047
 Average time/call (Set 6)
 String Parse: 00:00:00.0000054
 Enum Code: 00:00:00.0000231
 Spanish Code: 00:00:00.0000047
 Average time/call (Set 7)
 String Parse: 00:00:00.0000036
 Enum Code: 00:00:00.0000223
 Spanish Code: 00:00:00.0000048
 Average time/call (Set 8)
 String Parse: 00:00:00.0000039
 Enum Code: 00:00:00.0000220
 Spanish Code: 00:00:00.0000050
 Average time/call (Set 9)
 String Parse: 00:00:00.0000037
 Enum Code: 00:00:00.0000226
 Spanish Code: 00:00:00.0000049
Average time/call (Magnitude 1)
 String Parse: 00:00:00.0000042
 Enum code: 00:00:00.0000225
 Spanish code: 00:00:00.0000049
Input Set Magnitude: 10
 Average time/call (Set 0)
 String Parse: 00:00:00.0000040
 Enum Code: 00:00:00.0000222
 Spanish Code: 00:00:00.0000049
 Average time/call (Set 1)
 String Parse: 00:00:00.0000037
 Enum Code: 00:00:00.0000219
 Spanish Code: 00:00:00.0000051
 Average time/call (Set 2)
 String Parse: 00:00:00.0000040
 Enum Code: 00:00:00.0000223
 Spanish Code: 00:00:00.0000050
 Average time/call (Set 3)
 String Parse: 00:00:00.0000038
 Enum Code: 00:00:00.0000230
 Spanish Code: 00:00:00.0000050
 Average time/call (Set 4)
 String Parse: 00:00:00.0000038
 Enum Code: 00:00:00.0000226
 Spanish Code: 00:00:00.0000050
 Average time/call (Set 5)
 String Parse: 00:00:00.0000037
 Enum Code: 00:00:00.0000224
 Spanish Code: 00:00:00.0000049
 Average time/call (Set 6)
 String Parse: 00:00:00.0000038
 Enum Code: 00:00:00.0000221
 Spanish Code: 00:00:00.0000049
 Average time/call (Set 7)
 String Parse: 00:00:00.0000039
 Enum Code: 00:00:00.0000222
 Spanish Code: 00:00:00.0000050
 Average time/call (Set 8)
 String Parse: 00:00:00.0000037
 Enum Code: 00:00:00.0000223
 Spanish Code: 00:00:00.0000069
 Average time/call (Set 9)
 String Parse: 00:00:00.0000040
 Enum Code: 00:00:00.0000224
 Spanish Code: 00:00:00.0000052
Average time/call (Magnitude 10)
 String Parse: 00:00:00.0000038
 Enum code: 00:00:00.0000223
 Spanish code: 00:00:00.0000052
Input Set Magnitude: 100
 Average time/call (Set 0)
 String Parse: 00:00:00.0000044
 Enum Code: 00:00:00.0000270
 Spanish Code: 00:00:00.0000052
 Average time/call (Set 1)
 String Parse: 00:00:00.0000043
 Enum Code: 00:00:00.0000257
 Spanish Code: 00:00:00.0000054
 Average time/call (Set 2)
 String Parse: 00:00:00.0000042
 Enum Code: 00:00:00.0000263
 Spanish Code: 00:00:00.0000051
 Average time/call (Set 3)
 String Parse: 00:00:00.0000043
 Enum Code: 00:00:00.0000258
 Spanish Code: 00:00:00.0000053
 Average time/call (Set 4)
 String Parse: 00:00:00.0000045
 Enum Code: 00:00:00.0000265
 Spanish Code: 00:00:00.0000053
 Average time/call (Set 5)
 String Parse: 00:00:00.0000043
 Enum Code: 00:00:00.0000258
 Spanish Code: 00:00:00.0000052
 Average time/call (Set 6)
 String Parse: 00:00:00.0000043
 Enum Code: 00:00:00.0000259
 Spanish Code: 00:00:00.0000051
 Average time/call (Set 7)
 String Parse: 00:00:00.0000044
 Enum Code: 00:00:00.0000257
 Spanish Code: 00:00:00.0000051
 Average time/call (Set 8)
 String Parse: 00:00:00.0000043
 Enum Code: 00:00:00.0000260
 Spanish Code: 00:00:00.0000051
 Average time/call (Set 9)
 String Parse: 00:00:00.0000042
 Enum Code: 00:00:00.0000256
 Spanish Code: 00:00:00.0000051
Average time/call (Magnitude 100)
 String Parse: 00:00:00.0000043
 Enum code: 00:00:00.0000260
 Spanish code: 00:00:00.0000052
Input Set Magnitude: 1000
 Average time/call (Set 0)
 String Parse: 00:00:00.0000051
 Enum Code: 00:00:00.0000327
 Spanish Code: 00:00:00.0000058
 Average time/call (Set 1)
 String Parse: 00:00:00.0000051
 Enum Code: 00:00:00.0000312
 Spanish Code: 00:00:00.0000054
 Average time/call (Set 2)
 String Parse: 00:00:00.0000051
 Enum Code: 00:00:00.0000312
 Spanish Code: 00:00:00.0000052
 Average time/call (Set 3)
 String Parse: 00:00:00.0000052
 Enum Code: 00:00:00.0000309
 Spanish Code: 00:00:00.0000055
 Average time/call (Set 4)
 String Parse: 00:00:00.0000051
 Enum Code: 00:00:00.0000313
 Spanish Code: 00:00:00.0000053
 Average time/call (Set 5)
 String Parse: 00:00:00.0000051
 Enum Code: 00:00:00.0000308
 Spanish Code: 00:00:00.0000053
 Average time/call (Set 6)
 String Parse: 00:00:00.0000051
 Enum Code: 00:00:00.0000312
 Spanish Code: 00:00:00.0000053
 Average time/call (Set 7)
 String Parse: 00:00:00.0000050
 Enum Code: 00:00:00.0000310
 Spanish Code: 00:00:00.0000053
 Average time/call (Set 8)
 String Parse: 00:00:00.0000051
 Enum Code: 00:00:00.0000306
 Spanish Code: 00:00:00.0000053
 Average time/call (Set 9)
 String Parse: 00:00:00.0000050
 Enum Code: 00:00:00.0000323
 Spanish Code: 00:00:00.0000054
Average time/call (Magnitude 1000)
 String Parse: 00:00:00.0000051
 Enum code: 00:00:00.0000313
 Spanish code: 00:00:00.0000054
Input Set Magnitude: 10000
 Average time/call (Set 0)
 String Parse: 00:00:00.0000064
 Enum Code: 00:00:00.0000370
 Spanish Code: 00:00:00.0000061
 Average time/call (Set 1)
 String Parse: 00:00:00.0000062
 Enum Code: 00:00:00.0000360
 Spanish Code: 00:00:00.0000059
 Average time/call (Set 2)
 String Parse: 00:00:00.0000061
 Enum Code: 00:00:00.0000354
 Spanish Code: 00:00:00.0000058
 Average time/call (Set 3)
 String Parse: 00:00:00.0000060
 Enum Code: 00:00:00.0000354
 Spanish Code: 00:00:00.0000057
 Average time/call (Set 4)
 String Parse: 00:00:00.0000061
 Enum Code: 00:00:00.0000351
 Spanish Code: 00:00:00.0000057
 Average time/call (Set 5)
 String Parse: 00:00:00.0000063
 Enum Code: 00:00:00.0000361
 Spanish Code: 00:00:00.0000057
 Average time/call (Set 6)
 String Parse: 00:00:00.0000061
 Enum Code: 00:00:00.0000356
 Spanish Code: 00:00:00.0000059
 Average time/call (Set 7)
 String Parse: 00:00:00.0000061
 Enum Code: 00:00:00.0000363
 Spanish Code: 00:00:00.0000058
 Average time/call (Set 8)
 String Parse: 00:00:00.0000085
 Enum Code: 00:00:00.0000366
 Spanish Code: 00:00:00.0000060
 Average time/call (Set 9)
 String Parse: 00:00:00.0000061
 Enum Code: 00:00:00.0000352
 Spanish Code: 00:00:00.0000058
Average time/call (Magnitude 10000)
 String Parse: 00:00:00.0000064
 Enum code: 00:00:00.0000359
 Spanish code: 00:00:00.0000059
Input Set Magnitude: 100000
 Average time/call (Set 0)
 String Parse: 00:00:00.0000068
 Enum Code: 00:00:00.0000388
 Spanish Code: 00:00:00.0000061
 Average time/call (Set 1)
 String Parse: 00:00:00.0000069
 Enum Code: 00:00:00.0000397
 Spanish Code: 00:00:00.0000061
 Average time/call (Set 2)
 String Parse: 00:00:00.0000068
 Enum Code: 00:00:00.0000393
 Spanish Code: 00:00:00.0000060
 Average time/call (Set 3)
 String Parse: 00:00:00.0000068
 Enum Code: 00:00:00.0000393
 Spanish Code: 00:00:00.0000061
 Average time/call (Set 4)
 String Parse: 00:00:00.0000068
 Enum Code: 00:00:00.0000400
 Spanish Code: 00:00:00.0000061
 Average time/call (Set 5)
 String Parse: 00:00:00.0000069
 Enum Code: 00:00:00.0000401
 Spanish Code: 00:00:00.0000081
 Average time/call (Set 6)
 String Parse: 00:00:00.0000075
 Enum Code: 00:00:00.0000405
 Spanish Code: 00:00:00.0000061
 Average time/call (Set 7)
 String Parse: 00:00:00.0000072
 Enum Code: 00:00:00.0000404
 Spanish Code: 00:00:00.0000063
 Average time/call (Set 8)
 String Parse: 00:00:00.0000071
 Enum Code: 00:00:00.0000405
 Spanish Code: 00:00:00.0000062
 Average time/call (Set 9)
 String Parse: 00:00:00.0000068
 Enum Code: 00:00:00.0000399
 Spanish Code: 00:00:00.0000061
Average time/call (Magnitude 100000)
 String Parse: 00:00:00.0000070
 Enum code: 00:00:00.0000399
 Spanish code: 00:00:00.0000063
Average overall time/call
 String Parse: 00:00:00.0000051
 Enum code: 00:00:00.0000296
 Spanish code: 00:00:00.0000054

As you can see, these results are in line with my initial approximation. Now, although your routine (and tonhinbm's, for that matter) is not yet mature, I can't imagine that the small fixes necessary to bring it in line would substantially alter these results, can you Now, I have to confess to refining my code just a bit (since I had it out, you know ), but all it amounts to is making the second part of a compound name lower case (Twenty-nine, instead of Twenty-Nine), and increasing the capacity to hundreds of thousands. Certainly not anything that would make it faster.

Anyway, right away I saw the correlation between string literals and performance in tonhinbm's code, so I changed mine over to string literals instead of enums, and I got these results:

Input Set Magnitude: 1
	Average time/call (Set 0)
		String Parse: 00:00:00.0000048
		Literal Code: 00:00:00.0000040
		Spanish Code: 00:00:00.0000058
	Average time/call (Set 1)
		String Parse: 00:00:00.0000038
		Literal Code: 00:00:00.0000035
		Spanish Code: 00:00:00.0000048
	Average time/call (Set 2)
		String Parse: 00:00:00.0000041
		Literal Code: 00:00:00.0000037
		Spanish Code: 00:00:00.0000054
	Average time/call (Set 3)
		String Parse: 00:00:00.0000039
		Literal Code: 00:00:00.0000033
		Spanish Code: 00:00:00.0000049
	Average time/call (Set 4)
		String Parse: 00:00:00.0000038
		Literal Code: 00:00:00.0000034
		Spanish Code: 00:00:00.0000049
	Average time/call (Set 5)
		String Parse: 00:00:00.0000037
		Literal Code: 00:00:00.0000033
		Spanish Code: 00:00:00.0000048
	Average time/call (Set 6)
		String Parse: 00:00:00.0000036
		Literal Code: 00:00:00.0000033
		Spanish Code: 00:00:00.0000047
	Average time/call (Set 7)
		String Parse: 00:00:00.0000036
		Literal Code: 00:00:00.0000032
		Spanish Code: 00:00:00.0000047
	Average time/call (Set 8)
		String Parse: 00:00:00.0000038
		Literal Code: 00:00:00.0000033
		Spanish Code: 00:00:00.0000047
	Average time/call (Set 9)
		String Parse: 00:00:00.0000038
		Literal Code: 00:00:00.0000032
		Spanish Code: 00:00:00.0000047
Average time/call (Magnitude 1)
	String Parse: 00:00:00.0000039
	Literal code: 00:00:00.0000034
	Spanish code: 00:00:00.0000050
Input Set Magnitude: 10
	Average time/call (Set 0)
		String Parse: 00:00:00.0000037
		Literal Code: 00:00:00.0000035
		Spanish Code: 00:00:00.0000049
	Average time/call (Set 1)
		String Parse: 00:00:00.0000038
		Literal Code: 00:00:00.0000034
		Spanish Code: 00:00:00.0000049
	Average time/call (Set 2)
		String Parse: 00:00:00.0000037
		Literal Code: 00:00:00.0000034
		Spanish Code: 00:00:00.0000049
	Average time/call (Set 3)
		String Parse: 00:00:00.0000037
		Literal Code: 00:00:00.0000035
		Spanish Code: 00:00:00.0000048
	Average time/call (Set 4)
		String Parse: 00:00:00.0000037
		Literal Code: 00:00:00.0000034
		Spanish Code: 00:00:00.0000048
	Average time/call (Set 5)
		String Parse: 00:00:00.0000037
		Literal Code: 00:00:00.0000036
		Spanish Code: 00:00:00.0000067
	Average time/call (Set 6)
		String Parse: 00:00:00.0000041
		Literal Code: 00:00:00.0000034
		Spanish Code: 00:00:00.0000064
	Average time/call (Set 7)
		String Parse: 00:00:00.0000051
		Literal Code: 00:00:00.0000034
		Spanish Code: 00:00:00.0000049
	Average time/call (Set 8)
		String Parse: 00:00:00.0000038
		Literal Code: 00:00:00.0000034
		Spanish Code: 00:00:00.0000048
	Average time/call (Set 9)
		String Parse: 00:00:00.0000037
		Literal Code: 00:00:00.0000034
		Spanish Code: 00:00:00.0000049
Average time/call (Magnitude 10)
	String Parse: 00:00:00.0000039
	Literal code: 00:00:00.0000034
	Spanish code: 00:00:00.0000052
Input Set Magnitude: 100
	Average time/call (Set 0)
		String Parse: 00:00:00.0000045
		Literal Code: 00:00:00.0000036
		Spanish Code: 00:00:00.0000051
	Average time/call (Set 1)
		String Parse: 00:00:00.0000043
		Literal Code: 00:00:00.0000036
		Spanish Code: 00:00:00.0000052
	Average time/call (Set 2)
		String Parse: 00:00:00.0000044
		Literal Code: 00:00:00.0000036
		Spanish Code: 00:00:00.0000052
	Average time/call (Set 3)
		String Parse: 00:00:00.0000044
		Literal Code: 00:00:00.0000036
		Spanish Code: 00:00:00.0000052
	Average time/call (Set 4)
		String Parse: 00:00:00.0000045
		Literal Code: 00:00:00.0000036
		Spanish Code: 00:00:00.0000052
	Average time/call (Set 5)
		String Parse: 00:00:00.0000044
		Literal Code: 00:00:00.0000037
		Spanish Code: 00:00:00.0000051
	Average time/call (Set 6)
		String Parse: 00:00:00.0000045
		Literal Code: 00:00:00.0000037
		Spanish Code: 00:00:00.0000051
	Average time/call (Set 7)
		String Parse: 00:00:00.0000044
		Literal Code: 00:00:00.0000037
		Spanish Code: 00:00:00.0000052
	Average time/call (Set 8)
		String Parse: 00:00:00.0000043
		Literal Code: 00:00:00.0000038
		Spanish Code: 00:00:00.0000053
	Average time/call (Set 9)
		String Parse: 00:00:00.0000045
		Literal Code: 00:00:00.0000037
		Spanish Code: 00:00:00.0000052
Average time/call (Magnitude 100)
	String Parse: 00:00:00.0000044
	Literal code: 00:00:00.0000037
	Spanish code: 00:00:00.0000052
Input Set Magnitude: 1000
	Average time/call (Set 0)
		String Parse: 00:00:00.0000058
		Literal Code: 00:00:00.0000041
		Spanish Code: 00:00:00.0000059
	Average time/call (Set 1)
		String Parse: 00:00:00.0000055
		Literal Code: 00:00:00.0000041
		Spanish Code: 00:00:00.0000057
	Average time/call (Set 2)
		String Parse: 00:00:00.0000053
		Literal Code: 00:00:00.0000040
		Spanish Code: 00:00:00.0000053
	Average time/call (Set 3)
		String Parse: 00:00:00.0000053
		Literal Code: 00:00:00.0000041
		Spanish Code: 00:00:00.0000054
	Average time/call (Set 4)
		String Parse: 00:00:00.0000056
		Literal Code: 00:00:00.0000041
		Spanish Code: 00:00:00.0000054
	Average time/call (Set 5)
		String Parse: 00:00:00.0000052
		Literal Code: 00:00:00.0000041
		Spanish Code: 00:00:00.0000054
	Average time/call (Set 6)
		String Parse: 00:00:00.0000053
		Literal Code: 00:00:00.0000040
		Spanish Code: 00:00:00.0000054
	Average time/call (Set 7)
		String Parse: 00:00:00.0000052
		Literal Code: 00:00:00.0000040
		Spanish Code: 00:00:00.0000054
	Average time/call (Set 8)
		String Parse: 00:00:00.0000052
		Literal Code: 00:00:00.0000039
		Spanish Code: 00:00:00.0000054
	Average time/call (Set 9)
		String Parse: 00:00:00.0000052
		Literal Code: 00:00:00.0000040
		Spanish Code: 00:00:00.0000054
Average time/call (Magnitude 1000)
	String Parse: 00:00:00.0000054
	Literal code: 00:00:00.0000040
	Spanish code: 00:00:00.0000055
Input Set Magnitude: 10000
	Average time/call (Set 0)
		String Parse: 00:00:00.0000064
		Literal Code: 00:00:00.0000046
		Spanish Code: 00:00:00.0000059
	Average time/call (Set 1)
		String Parse: 00:00:00.0000063
		Literal Code: 00:00:00.0000045
		Spanish Code: 00:00:00.0000058
	Average time/call (Set 2)
		String Parse: 00:00:00.0000062
		Literal Code: 00:00:00.0000044
		Spanish Code: 00:00:00.0000071
	Average time/call (Set 3)
		String Parse: 00:00:00.0000092
		Literal Code: 00:00:00.0000048
		Spanish Code: 00:00:00.0000059
	Average time/call (Set 4)
		String Parse: 00:00:00.0000065
		Literal Code: 00:00:00.0000046
		Spanish Code: 00:00:00.0000058
	Average time/call (Set 5)
		String Parse: 00:00:00.0000064
		Literal Code: 00:00:00.0000045
		Spanish Code: 00:00:00.0000058
	Average time/call (Set 6)
		String Parse: 00:00:00.0000064
		Literal Code: 00:00:00.0000045
		Spanish Code: 00:00:00.0000058
	Average time/call (Set 7)
		String Parse: 00:00:00.0000062
		Literal Code: 00:00:00.0000044
		Spanish Code: 00:00:00.0000058
	Average time/call (Set 8)
		String Parse: 00:00:00.0000062
		Literal Code: 00:00:00.0000046
		Spanish Code: 00:00:00.0000059
	Average time/call (Set 9)
		String Parse: 00:00:00.0000065
		Literal Code: 00:00:00.0000045
		Spanish Code: 00:00:00.0000059
Average time/call (Magnitude 10000)
	String Parse: 00:00:00.0000066
	Literal code: 00:00:00.0000045
	Spanish code: 00:00:00.0000060
Input Set Magnitude: 100000
	Average time/call (Set 0)
		String Parse: 00:00:00.0000072
		Literal Code: 00:00:00.0000049
		Spanish Code: 00:00:00.0000062
	Average time/call (Set 1)
		String Parse: 00:00:00.0000071
		Literal Code: 00:00:00.0000048
		Spanish Code: 00:00:00.0000063
	Average time/call (Set 2)
		String Parse: 00:00:00.0000071
		Literal Code: 00:00:00.0000050
		Spanish Code: 00:00:00.0000062
	Average time/call (Set 3)
		String Parse: 00:00:00.0000074
		Literal Code: 00:00:00.0000048
		Spanish Code: 00:00:00.0000065
	Average time/call (Set 4)
		String Parse: 00:00:00.0000072
		Literal Code: 00:00:00.0000048
		Spanish Code: 00:00:00.0000062
	Average time/call (Set 5)
		String Parse: 00:00:00.0000072
		Literal Code: 00:00:00.0000050
		Spanish Code: 00:00:00.0000062
	Average time/call (Set 6)
		String Parse: 00:00:00.0000072
		Literal Code: 00:00:00.0000047
		Spanish Code: 00:00:00.0000062
	Average time/call (Set 7)
		String Parse: 00:00:00.0000071
		Literal Code: 00:00:00.0000048
		Spanish Code: 00:00:00.0000062
	Average time/call (Set 8)
		String Parse: 00:00:00.0000072
		Literal Code: 00:00:00.0000047
		Spanish Code: 00:00:00.0000061
	Average time/call (Set 9)
		String Parse: 00:00:00.0000071
		Literal Code: 00:00:00.0000047
		Spanish Code: 00:00:00.0000062
Average time/call (Magnitude 100000)
	String Parse: 00:00:00.0000072
	Literal code: 00:00:00.0000048
	Spanish code: 00:00:00.0000062
Average overall time/call
	String Parse: 00:00:00.0000052
	Literal code: 00:00:00.0000039
	Spanish code: 00:00:00.0000055

Plainly, string literals are an order of magnitude faster in this case. Which just brings us back around to the old flame-starter, "where do you define the compromise between performance and maintainability " In this particular case, I see good arguments both ways.

On the one hand, using enums, generating a million paychecks would take roughly 30 seconds, and in almost any imaginable scenario, the bottleneck would be the printer, not the code.

On the other hand, this is a purpose-built tool, and the only realistically expected maintenance programming it would need would be adapting it to different output languages; which, as tonhinbm's Spanish adaptation shows, would almost invariable result in a substantial, if not major, rewrite (the special 20's case for Spanish, for example).

Again, if it's fast enough for the UI, it's usually fast enough to suit me, and besides, I generally dump the real slow-pokes onto another thread, so the difference between 3 microseconds and 30 microseconds might as well be nil.

The main thing is that I have satisfied my curiosity, and now maybe I can get a good night's sleep! Add to that the bonus of having learned quite a bit with this little exercise, and I'm happy as a clam.






Re: Show and Tell Convert Money value to its Textual Representation

OmegaMan

Hi Mark,

I did find one error with cents, any cent ending with 1 such as 61, 51 showed cent. I fixed that with this line


result.AppendFormat("and {0}cent{1}", ShowMe(cents, false), ((cents[1] != '0') && (cents[0] != '1') "s" : string.Empty));


Other than that, nothing jumped out at me except upon reading the data, the max line size could be 81 characters, so the 64 setting for stringbuilder was wrong. For the real tests, once I removed the Console writes, the processing really moved. For fun I removed the stringbuilder and did it all with string concatenation.

Here are my results

StringBuilder (64) Average time/call: 00:00:00.0000055
StringBuilder(90) Average time/call: 00:00:00.0000054
String Average time/call: 00:00:00.0000066

Not a real difference from 64 to 90 and a slight difference using just string concatenation! Note tests run on a Core Duo 2.16 (T2600).







Re: Show and Tell Convert Money value to its Textual Representation

OmegaMan

After posting mine, I saw you posted yours! Great work. I am very impressed with the test setup. Sorry for not getting back to you sooner! I concur with your thoughts 100%. Only in going to different languages is the only real downside.





Re: Show and Tell Convert Money value to its Textual Representation

Mark Benningfield

Hello All.

Omega Man:

OmegaMan wrote:
Hi Mark,

I did find one error with cents, any cent ending with 1 such as 61, 51 showed cent. I fixed that with this line


result.AppendFormat("and {0}cent{1}", ShowMe(cents, false), ((cents[1] != '0') && (cents[0] != '1') "s" : string.Empty));


I modified your routine with the line quoted above, and it did make quite an improvement. There are only a couple of anomalies left, which I hope you understand I'm pointing out, not to nitpick, but to help. As near as I can make out (my Spanish is not as strong as it used to be), the only anomaly in tonhinbm's code is that the zero case-handling needs to be strengthened a bit. It's kinda all over the place.

Here's a sample:

0.19 zero dollars and nine cent
9.50 nine dollars and fifty-cent
0.10 zero dollars and ten cent
0.13 zero dollars and three cent
3.13 three dollars and three cent
28.14 twenty-eight dollars and four cent
51.19 fifty-one dollars and nine cent
37.15 thirty-seven dollars and five cent
16.70 six dollars and seventy-cent
19.95 nine dollars and ninety-five cents
80.68 eighty-dollars and sixty-eight cents
14.16 four dollars and six cent
850.70 eight hundred fifty-dollars and seventy-cent
916.14 nine hundred six dollars and four cent
292.14 two hundred ninety-two dollars and four cent

0.40 Cero EUROS Con Cuarenta CENTIMOS
0.75 EURO Con Setenta y Cinco CENTIMOS
0.39 Cero EUROS Con Treinta y Nueve CENTIMOS
0.07 Cero EUROS Con Siete CENTIMOS
0.51 EURO Con Cincuenta y Un CENTIMOS
0.72 EURO Con Setenta y Dos CENTIMOS
0.16 Cero EUROS Con Dieciseis CENTIMOS
0.95 EURO Con Noventa y Cinco CENTIMOS
0.78 EURO Con Setenta y Ocho CENTIMOS
0.59 EURO Con Cincuenta y Nueve CENTIMOS

This sample was excerpted from the output file produced by running the test harness, which was modified simply to write the input value and the result of the method call. As I say, it looks like there are only two quirks left in your routine, and I wouldn't think that the fix would result in any appreciable performance loss. The same goes for tonhinbm's routine, as well, I would imagine. So, I would have to say that the test results still ought to be considered valid.

HTH.






Re: Show and Tell Convert Money value to its Textual Representation

OmegaMan

There are only a couple of anomalies left, which I hope you understand I'm pointing out, not to nitpick, but to help.


No offense taken! It sounds like we both have been in the industry long enough to know its the final goal, bug free output to the user, and its better that it is found early and not later.

As I say, it looks like there are only two quirks left in your routine, and I wouldn't think that the fix would result in any appreciable performance loss.


I will take the anomalies you provided and work them out and get back to you in a couple of days as time permits in my schedule.

As an aside, with bug findings, it reminds me of my first job where I had to program graphics primitives to build a GUI interface for a workstation class computer of the time. I built two buttons for pressing and the code to handle the user selections and handed it off to another developer. The developer said, looks great and works great until the user clicks between the buttons...and they both go down!




Re: Show and Tell Convert Money value to its Textual Representation

Mark Benningfield

Hello All.

OmegaMan:

I swear, life would be much simpler if the cotton-pickin' users would just learn to behave themselves!!

LOL.