Flag values are a bad idea. Flag values that are floating point are doubly so, even if double precision.
If you are using IEEE double precision floating point values, the values -1
and -2
are exactly representable as double
s, and comparison is well defined. No "magic error" will slip in if you merely copy the double
around or only read the value. In fact, on a system with conventional 2s complement 32 bit int
s, every int
can be represented exactly as a IEEE double precision floating point value.
Now, transformations you think wouldn't matter like x /3. * 3.
will ruin the identity, so the code is very fragile: fragile both because flag values are fragile, and because floating point equivalence is often fragile in practice.
In C++, there are a myriad of ways to do this that are less fragile.
enum error_code {a,b,c};
boost::variant<double, error_code> getValue();
is a tagged union that can hold either a double
or an error_code
. There is a std::expected
proposal you can look at which is a tagged union with a "bias" towards the first value being the only valid one (sort of a cross between std::experimental::optional
and boost::variant
).
Both of these result in the value being returned in a type-safe way, where an error is a different type of value than the non-error return type.
Alternative solutions include returning the error code separately (as the return value, or taking a pointer-to-error-code as a parameter (what I call ICU style)). The double
could be set to some innocuous value in that case (say, NaN
) rather than left uninitialized.
double getValue( error_code* e );
or
error_code getValue( double* out );
where enum error_code { a = -1, b = -2 }
is an enumeration of the error codes.