The stl::string class has >100 member functions. That doesn't make monolithic class design a Good Thing.
Macros don't care about scope, or type, or pretty much anything. Using them is not safe and contradicts encapsulation. They don't belong into C++, even though needed/useful in C. C++ has a type-safe and scope-safe solution for all the problems macros possibly can offer, without even being more complex:
template<typename T> equal(const T& t1, const T& t2) {return abs(t2-t1)<epsilon(t1,t2);}
... where you now could easily overload the epsilon function for double, float, whatever.
I also would question your "without issues" statement. It still isn't safe/portable to use the '_' in the beginning of a name, even if not in global scope, because some compiler vendors use names starting with a single '_' in macros (thinking that they are in "global scope", but there is no such thing as a scope for a macro). Since these names are reserved in global scope only, this actually *is* an issue (although not for the one writing the macro...).
I use them only for doubles, and it's 1e-10 because anything less than that is considered scientifically insignificant in my work so it is not a worry. |
If you would write a program for me, and you wouldn't document the input domain and the error tolerance of the output, I'd consider such a fixed, undocumented epsilon a bug. And being honest, do you really know, when writing your code, what the error of an expression like a*b+c/d is for a, b,c and d in the range [-1000, 1000] would be? I don't, but I'm very happy that my semantic epsilon function does. (Also note that being "insignificant for the output" does not imply "insignificant for the calculations")