Software without bugs #2

| | Comments (8)

Earlier, I challenged everyone to write a bug-free program that appears to be trivial. All it has to do is raise one number to an arbitrary power and display the results. However, bug-free is a very difficult thing to achieve, as anyone with experience in software can agree to.

Here's a set of of the most common bugs that this particular project suffers from:

1) Since it's meant to provide *arbitrary* exponents, you cannot assume that an Integer input is sufficient. What if the user really wanted to do a square root by passing in .5? So you need accept floating point arguements as well as return a floating point result.

2) Well, if the user can produce square roots, etc -- what should happen when the user does: MyPow( -1, .5 )? That's akin to Sqrt( -1 ), which produces a result we cannot express. So are you handling the error properly? If you are handling the error, what are you returning to the user?

3) If you did remember to handle #2 properly, are you sure you only do this check for even values of 1/x? In math, you can take the cube root of a negative number, but not the square or quadratic root. I noticed a bug in the Microsoft version of their calculator which considers all negative bases illegal when doing roots, and this same bug is present in the ANSI C version of the pow function. Ref. However, in all fairness, the documentation for the pow function is explicit about its behavior; so the term "bug" is used loosely here: I thought the function would adhere to math standards. Just because it doesn't isn't a bug, it's mismatched expectations.

4) Are you certain your output is correct for MyPow( 5, 2.3 )? The answer should be roughly 40.516414917319 if you're interested.

5) Be cautious of using recursion as it can exhaust stack space. It's not that you can't use recursion, mind you. You just need to watch out for stack overflow exceptions and handle them gracefully.

As you can see, it's not quite as easy as it looks. The point of this challenge is to demonstrate that even the easiest tasks can actually be very complicated. So the next time you find yourself saying, "how can there be so many bugs?" (whether it's with REALbasic, the OS, or any project), you should keep this exercise in mind. This was just one simple function, after all -- not a complex project consistent of millions of lines of code, and even the ANSI C implementation of the pow function couldn't get it right by mathematical standards!

The simple fact of the matter is that bug free code is essentially a unicorn -- you'll never find it in the wild (unless you make it by hand using antelope antlers, a white Arabian horse and some splicing implements!).

Truth be told, I spent four hours monkeying around with this very project and was unable to come up with an implementation that didn't contain bugs. I could get the simple case done, ignoring all of the points I discussed above. Where it really fell apart was doing arbitrary roots to handle the fractional part of the exponent. So, things like MyPow( 8, 1.6 ) would come out entirely wrong. The general solution to this is to write the exponent out in fractional form (16/10), and then solve: (8^(1/10))^16. That's not too hard to accomplish, but writing your own arbitrary root algorithm by hand starts to get hair really quickly. So I never even got to the part where I could handle negative bases with fractional exponents!

8 Comments

I tried to find a good algorithem for arbitrary roots and powers, but I didn't find much. There are good ones for integers, but what about irrational numbers? how are you suppose to deal with those? 1.6 is easy enough, but what about pi? That is a valid number. I could dive into what libmath does (it's newer than ANSI C), but that would probably be a pain. They probably have some decision trees for cases and then have lookup tables for weird numbers to get close enough.

@jdiwnab -- the general rule of thumb is "close enough works fine." For instance, IEEE 64-bit doubles can only store up to 52 bits of the fractional part, which is 23 digits, I believe. So anything beyond 23 digits is going to be wasted precision anyhow since a Double cannot encode that information.

so would you take the first 23 digits of pi, or instance and do something like (8^(1/100000000000000000000000))^3
1415926535897932384626 (that's 10^23 and the first 23 digits of Pi) ? It seems like that would cause all kinds of problems with stacks and time. Even if you take only the first, say, 10 digits, that's still a lot. I still think there is a good, general algorithm for this out there that will work. I'm just trying to figure out what it is. This doesn't seem like it.

@jdiwnab -- there's probably a much better way to do it than what I was saying. ;-) The interesting part of this project wasn't that the algorithm itself. It was that the problem seemed simple on its face, but could be full of pitfalls. It was meant to demonstrate that it's very easy to get "buggy software" with even simple projects.

And there is the entire area of numerical precision which in and of itself can cause obscure bugs :)

"Cumulative rounding error" is not something any accountant wants to hear when you write financial applications in C :)


"The simple fact of the matter is that bug free code is essentially a unicorn"

So is a forum that's free of people with complaints that find it hard to be diplomatic, or even refuse to.

Gosh that thread (the one that sparked this challenge) was such a long read. See what I get for turning off my computer for 4 days, I nearly missed the whole thing! I spent an hour reading it. They get to be so predictable yet so entertaining. The worst part is when they start to personally attack each other. I mean why does either side even try. There is no convincing people when they know they are right. The proponents act like they are the ones that wrote RB and the Negetorions (i just made that up!) act like their worst enemy wrote it.

One of the issues with that entire discussion is that it seems to always come off as "US" vs "THEM" when it's not that simple.

There are a number of fence sitters in the crowd; and I tend to be one much more so now than I had been say 10 years ago when I first started.

Rb's not "perfect" - whatever that means - we know that.
It has bugs. We all know & admit that. The issue is what steps can & are being taken by the community to help make the product better and what steps can & are being taken by REAL to make the product better.
Improving the process to making RB better will help everyone make RB better than any rant on the forums or here ever will.

There will always be bugs in any software no matter how well it has been written. The question is: how far are you willing to go to get it as "perfect" as possible?

Do the little things matter to you? They should because a lot of little things add up. After all, if you can't get the easiest GUI bug fixed in your software, what does that say to your customers about the rest of it?

Aaron, looking at some code you've written, I can happily say that you are good at what you do and are not sloppy by any means - so I'm not putting you down or anything. It just gets pretty disappointing when a new feature arrives and an old bug hasn't been fixed because of "prioritization".

RB sometimes seems like a run-away train and if something gets fixed while it's rolling by at 200mph, then great. If not, then oh well - by the way, we're adding another couple cars to it! Does your boss threaten you with unemployment or a lashing if XYZ isn't done in time? lol. I hope not :-) ukdswcsi

Leave a comment

Disclaimer

I'm currently an employee of REAL Software. My blog is mine. The opinions represented in this blog are mine as well and may not represent my employer's opinions. All original material is copyrighted and property of the author.

REALbasic® is a registered trademark of REAL Software, Inc. REAL SQL Server™ and Lingua™ are pending trademarks of REAL Software, Inc. All rights reserved.