0

I wonder if I can return error code as double in the following way in C:

double getValue()
{
    double ret = 0;

    if (error1)
    {
        return -1;
    }
    if (error2)
    {
        return -2;
    }
    return ret = ....;
}

int main(void)
{
    double val = getValue();

    if (-1 == val)
    {
        printf("getValue: error1\n")
        return -1;
    }
    else if (-2 == val)
    {
        printf("getValue: error2\n");
        return -2;
    }

    ......
    return 0;
}

so when the return value is >= 0 then it is correct value which can be used for calculations. When value is less than zero error occurred. Will I get the floating-point-comparison problem when I compare return value with -1 or -2 ?

Clifford
  • 88,407
  • 13
  • 85
  • 165
emba
  • 1
  • 2

4 Answers4

3

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 doubles, 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 ints, 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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Excellent except just one thing I think "No 'magic error' will slip in" is potentially misleading, given the stuff you cover in the following paragraph. – Lightness Races in Orbit Mar 17 '15 at 21:24
  • @LightnessRacesinOrbit Well, it isn't "magic". It happens from pretty well defined operations. I guess saying explicitly some safe operation is a good idea: in particular, copying around is safe, reading is safe. – Yakk - Adam Nevraumont Mar 17 '15 at 21:26
  • If you want to use your initial idea then I'd return -1.1,-2.1,-3.1 etc. This return value will always be in the range (n,n+0.5) for any integer n converted to it's corresponding double. – phil Mar 18 '15 at 07:27
2

@LightnessRacesinOrbit beat me to it, but having typed it I post it anyway.

You can do it by taking the value to be set as a pointer argument, and returning a status. That way, no values of *ret are barred.

int getValue(double *ret)
{
    *ret = ...;

    if (error1)
        return -1;
    if (error2)
        return -2;

    return 0;
}

Then the calling code can be such as

double myval;
int err;
if ((err = getValue(&myval)) == 0)
    printf ("getValue() returned %f\n", myval);
else
    printf ("getValue() returned error %d\n", err);
Weather Vane
  • 33,872
  • 7
  • 36
  • 56
  • I have to be more precise. I can not use int instead of double. So let's assume the following comparison in C/C++ language: double a = -1; if (-1 == a) { printf("a=-1\n"); } so I set -1 to double variable in one place in the code, to indicate that something wrong happened and in the other place of the code I want to check this. Will "if" statement work as ecpected? Thanks for help. – emba Mar 17 '15 at 21:04
  • @emba you are getting *two* values. The `double *ret` you provide as a function argument, and a status, the function return value. I updated answer. – Weather Vane Mar 17 '15 at 21:11
1

Yes, you could get floating-point errors.

So consider using exceptions instead, or perhaps return an int error code and populate a double "out parameter" on success:

int getValue(double& ret)
{
    if (error1)
        return -1;
    if (error2)
        return -2;

    ret = ....;
    return 0;
}
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    Are you assuming a strange floating point system, or IEEE double precision floating point values? Because `-1` and `-2` are perfectly representable as IEEE double precision floating point values. – Yakk - Adam Nevraumont Mar 17 '15 at 20:47
  • @Yakk I'm assuming nothing. Relying on strict equality checks for any `double` is silly. Sure, in this particular, precise example, there are no "calculations" to speak of. But who knows how that may change in future? What if he starts calculating error codes from something fractional? Error can creep in regardless of the value you _meant_ to end up with being perfectly representable. – Lightness Races in Orbit Mar 17 '15 at 20:48
  • I'm not going to bother going into detail about that here, though; feel free to write your own answer! – Lightness Races in Orbit Mar 17 '15 at 20:49
  • Done! Btw, `double& ret=0`? Them's an interesting reference. :) – Yakk - Adam Nevraumont Mar 17 '15 at 21:18
0

Doing that is not necessary and makes error handling difficult, you should create an enum where you can add or remove error codes as needed, and also you don't really need to remeber what -1 is or what does -2 mean, just give each error a descriptive name, and do this

enum ErrorCodes {NoError, Error1, Error2, ... , ErrorN};
enum ErrorCodes getValue(double *value)
 {
    if (error1)
        return Error1;
    if (error2)
        return Error2;
    .
    .
    .
    if (errorN)
        return ErrorN;
    *value = resultOfCalculation;

    return NoError;
 }

then

enum ErrorCode code;
double         value;

code = getValue(&value);
switch (code)
 {
    case NoError:
        /* use value here */
        break;
    case Error1:
        /* handle error 1 */
        break;
    /* and so on */
 }

I think this is a lot better and elegant, because you can apply it anytime you want to have robust error checking, no matter what type the target value is, this would work exactly the same for a struct or a double or an int array.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97