Spot the Bug Contest #1 Solution: The mystery of the Decimal.Parse FormatException
Well - as promised here is the solution for the Spot the Bug (contest?) posting which was correctly answered first by Nat (Congratulations!).
The Bug:
The following line of code will fail intermittently when executed and most of the time rerunning will work. Sadly, these are the sort of bugs most easily overlooked.
return Decimal.Parse(rand.NextDouble().ToString());
where rand is a Random class instance. The exception thrown is:
FormatException Input string was not in a correct format.
What is bewildering is that on the face of it this seems like a perfectly legitimate line of code - we are simply converting a value from a Double in the range 0.0 - 1.0 (since that is what NextDouble() returns) to a Decimal value. The boundary conditions are both well contained within the Decimal range.
The only way out is to write a quick and dirty app to simulate this failure and here is what I wrote (no comments for sake of brevity):
static void Main(string[] args)
{
Decimal decNum; Random rand = new Random(); String str = "";
for (long counter = 0; counter < long.MaxValue; counter++)
{
try
{
decNum = Decimal.Parse(rand.NextDouble().ToString());
if (counter % 1000 == 0) Console.WriteLine("Iteration: " + counter);
}
catch (FormatException ex)
{
Console.WriteLine("Iteration: " + counter + "\nDouble value string: " + str);
Console.WriteLine("Exception occured: " + ex.ToString());
}
}
}
Running this console app with a breakpoint set inside the catch block, immediately bubbles up the root cause. The crux of the issue turns out to be that this exception occurs when the random Double value generated is so small as to need scientific notation. E.g. the double 0.000083304941692997209
will return "8.33049416929972E-05" when you do a Double.ToString() and this scientific representation is in turn not an acceptable input to the Decimal.Parse method, causing it to throw the FormatException. Mystery solved.
The Fix:
return (Decimal)rand.NextDouble();
Seems like the simpler and more logical way to write this in the first place, but that's only because this is a simplified case for illustration of this bug.
I already have another spot the bug lined up - just need some time to write it up. In case you have any such interesting bugs that you have hit as well (solved or not) that you would like to share, let me know!