|
|
|
|
|
by ajax77
5198 days ago
|
|
A slight variation with neither error codes nor exceptions. template< typename T >
auto findRoots(T a, T b, T c) -> pair<maybe<complex<T>>, maybe<complex<T>>> {
typedef complex<T> C;
typedef maybe<C> Root;
typedef pair<Root, Root> Roots;
if (a == 0) {
if (b == 0)
return Roots(nothing, nothing);
else
return Roots(Root(-c / b), nothing);
} else {
auto d = C(b*b - a*c*4);
auto two_a = a*2;
return Roots(Root((-b + sqrt(d)) / two_a),
Root((-b - sqrt(d)) / two_a));
}
}
maybe is simply a templatized std::pair wrapper that lets you check if the returned value is valid (either via maybe.valid() or if(maybe)). All imaginary results are handled automatically via std::complex. nothing decomposes into an invalid maybe value of the appropriate type. |
|
More importantly, your implementation is numerically less correct than my version, which itself is less correct than it should be.
Consider when b is near sqrt(d). In that case, b-sqrt(d) can lose precision. That's why I used the copysign function, so that I'm always adding two numbers of equal sign and size.
Mine is incorrect if b2 is near the size of 4ac since there too b2-4ac loses precision. Ideally this intermediate result should be done in quad precision if the input is in double.
Question: How does C++ handle copysign() (vs. copysignf() for floats), and support templates which want to use an higher precision intermediate value?