I sent this to Alex a few months ago but he didn't post it, probably
because it's sample code or he has too many others, so I thought I'd
share it with y'all.
I found a bug-hunting article on InformIT the other day that asks us to
find a minor semantic bug in one of the worst-designed functions I've
ever seen.
http://www.informit.com/articles/article.asp?p=342034&seqNum=2
1. public class IsLeapYear {
2.
3. public static class LeapYearException
4. extends Exception {}
5. public static class NotLeapYearException
6. extends Exception {}
7.
8. static void checkLeapYear(String year)
9. throws LeapYearException, NotLeapYearException,
10. NumberFormatException {
11.
12. long yearAsLong = Long.parseLong(year);
13.
14. //
15. // A leap year is a multiple of 4, unless it is
16. // a multiple of 100, unless it is a multiple of
17. // 400.
18. //
19. // We calculate the three values, then make a
20. // 3-bit binary value out of them and look it up
21. // in results.
22. //
23.
24. final boolean results[] =
25. { true, false, false, true,
26. false, false, false, false };
27.
28. if (results[
29. ((((yearAsLong % 4) == 0) ? 1 : 0) << 2) +
30. ((((yearAsLong % 100) == 0) ? 1 : 0) << 1) +
31. ((((yearAsLong % 400) == 0) ? 1 : 0) << 0)]) {
32. throw new LeapYearException();
33. } else {
34. throw new NotLeapYearException();
35. }
36. }
37.
38. public static void main(String[] args) {
39.
40. if (args.length > 0) {
41.
42. try {
43. checkLeapYear(args[0]);
44. } catch ( NumberFormatException nfe ) {
45. System.out.println(
46. "Invalid argument: " +
47. nfe.getMessage());
48. } catch ( LeapYearException lye ) {
49. System.out.println(
50. args[0] + " is a leap year");
51. } catch ( NotLeapYearException nlye ) {
52. System.out.println(
53. args[0] + " is not a leap year");
54. }
55. }
56. }
57. }
Whoa, that was awesome, Firefox pasted it in formatted and everything. Cool.
The error is that the table lookup is reversed, and gives the wrong
answer every time. The real bug... worst use of exceptions, worst use
of bitshifts, and all this complexity for the equivelent of a one-line
C macro. Can you say "just because I could"?
return (year % 4
== 0 && year % 100 != 0 && year % 400 == 0); does the
job quite nicely in my experience. (You'll want to test that it's a number and not null first.) Or Date.isLeapYear().