Hi, folks.

I would like to start posting a few C++ code snippets and thought I would start with something well-known and simple: a Quadratic Equation Solver.

I am trying to be as thorough as possible, so am also considering the possibility that a non-quadratic equation might be fed into the sub-routine.

For example, my sub-routine accepts the coefficients a, b, and c as per the standard form: ax^2 + bx + c = 0

To handle the case where a = 0 is input into the routine, I have left the components of one root unassigned. I was hoping that the output results would be something like 0xABB or some other gibberish, to highlight the fact that only one root exists, the other does NOT. However, I notice after testing the program with a = 0, the non-existent root is output as 9.25596313493178e+061. Most people would realize this is a non-answer, but I would like something that stands out more blatantly.

So I am considering using quiet_NaN. Is this acceptable practice? Or is there a better way someone can recommend?

Consider returning a vector or some other variable-length list of roots, which would also scale well to a more general root-finding algorithm.

Are you suggesting that I calculate the roots first and, if I discover that one doesn't exist, just reduce the size of the results vector and not even mention the fact that another root doesn't exist?

Are you suggesting that I calculate the roots first and, if I discover that one doesn't exist, just reduce the size of the results vector

More like as you discover them, add them to the list and return it when you're done. How practial this might be depends on your solver algorithm.

and not even mention the fact that another root doesn't exist?

Basically yes... this does require callers to know how many roots they should normally expect. This is how I'd build a general-purpose root finder, but it may not be the best for your specific scenario.

A question: What would your solver do with a quadratic equation where a != 0 but there is still only one root (e.g., f(x) = x^2)?

What would your solver do with a quadratic equation where a != 0 but there is still only one root (e.g., f(x) = x^2)?

That's called a repeated root, there are still two roots, they just have the same value. A proper quadratic equation solver should output two roots with the same value. Otherwise, it would be very weird.

So I am considering using quiet_NaN. Is this acceptable practice?

Acceptable? Yes.
Typical? Yeah..
People often use NaN or infinity as a cheap way to mark a double/float as "invalid". After all, that's what NaN is (Not-a-Number), although it technically should only result from specific operations like log(-1.0) or 0.0/0.0, you can use it for other reasonable cases (which usually derives, one way or another, from the basic cases that generate NaN or infinite values).

In this case, actually, the second root is infinity because it is a division by zero, from the following:

x = (-b +- sqrt(b^2 - 4ac)) / 2a

(assume b is positive, without loss of generality)

x1 = (-b + sqrt(b^2 - 4ac)) / 2a
x1 = 0 / 0
x1 = lim(a->0) (-b + sqrt(b^2 - 4ac)) / 2a
(use L'Hôpital's Rule)
x1 = lim(a->0) (-2 c / sqrt(b^2 - 4ac) / 2)  =  -c / b   (1st root)

x2 = (-b - sqrt(b^2 - 4ac)) / 2a
x2 = -b / a  =  -b / 0  =  -infinity   (2nd root)

So, technically-speaking, the value of the second root should be infinity, with the inverse sign of b.

But even if there wasn't a clear mathematical reason for the NaN or infinity value, it is totally fine to use it as a marker for "invalid". At least, I don't see a problem with it.

Or is there a better way someone can recommend?

Better, I'm not sure. Any other method is going to involve some additional overhead somewhere. Here are some of the typical solutions (for this problem and other similar situations):

  • Output the two roots and a "number of roots found" integer value (if only one exists, just leave the second root with some undefined value or just zero).
  • Accept an iterator (or pointer) to the solution container as a parameter to the function, and output the final iterator after having set all the roots that were found (i.e., the distance between the iterator that was passed in and the iterator that was returned gives you the number of roots found).
  • Use a discriminated union type such as boost::variant< std::complex<double>, char >.

As you see, these are ugly solutions and have overhead. The thing is, you really just need 1 bit to encode the valid/invalid state of the number, and there's no elegant way to add a bit to anything. Since floating point numbers already have a few specific bit patterns (NaN, +-INF) dedicated to marking a value as invalid, you might as well use them. And people do this all the time, so, it's pretty much an established practice. Of course, you should document it.

Edited 3 Years Ago by mike_2000_17: assumption

Thanks for all the feedback.

I just posted my code snippet and I used quiet_NaN() after all.
I wanted to make sure, if a = 0, the user didn't miss the fact that only one root would exist. Hopefully, I have made it blatant enough so that the user doesn't miss it. If he misses the output statement, hopefully when viewing the results and seeing a "NaN" result, he will think, "What the ...?" and go back to look at the inputs.

This article has been dead for over six months. Start a new discussion instead.