0

Lets say for example we have a function that does some math (for the sake of argument, multiply two numbers). It returns the result but if there is an error, the function returns -1.

However, -1 can actually a valid result (-1 * 1 for example). What is a better constant to return? I was thinking of returning INT_MAX on error but some of my math functions return double and float so I wanted to see if there was a better alternative.

This is for a library so I want to use the same constant for errors to minimize confusion.

Katianie
  • 589
  • 1
  • 9
  • 38

4 Answers4

2

The usual solution is to use the return value only to indicate success or error, and return the actual result via a pointer:

int multiply(int a, int b, int *result_out)
{
    if (/* success... */) {
        *result_out = a * b;
        return 0;
    } else {
        return -1;
    }
}
dpi
  • 1,919
  • 17
  • 17
  • I guess but that's weird and I want people to be able to pickup and use my library (somewhat) easaly. – Katianie Oct 21 '16 at 16:07
  • 2
    @Katianie: if they have to check for an error and a result, it is (at least) slightly messy no matter what you do in C. In C++, you can raise an exception in case of error. This is often the best way to deal with it in C, though. – Jonathan Leffler Oct 22 '16 at 00:52
1

Floats and doubles can be NaN

NaN = Not a number.

You may want to read this: How to use nan and inf in C?

https://en.wikipedia.org/wiki/NaN

Set errno

Not all functions return floating point values, so NaN can't always be used.

Since some math functions can return any number of their type, you can't really use the return value to indicate that an error has occurred.

You could still unset and set errno. It does have the side effect that old values of errno will be overwritten.

In example.h:

#include <errno.h>

/* extern int errno; */

double division(double n, double d);

In example.c:

#include "example.h"

double division(double n, double d)
{
    if (0 == d)
    {
        errno = EDOM;
        return 0.0;  /* Does not matter. */
    }
    else
        return n/d;
}

In main.c:

#include <stdio.h>
#include "example.h"

int main(int argc, char *argv[])
{
    division(1.0, 0.0);
    if (EDOM == errno)
    {
        fprintf(stderr, "Couldn't divide 1.0 by 0.0\n");
        errno = 0;  /* Reset so it won't appear that the error has
                       occurred even when it hasn't. */
    }
    division(3.14, 2.78);
    if (EDOM == errno)
    {
         fprintf(stderr, "Couldn't divide 3.14 by 2.78.\n");
         errno = 0;
    }
    return 0;
}

Set an error flag

Or you could use a global variable of your own that you don't unset if no error has occured.

This would allow you to make a whole bunch of calls to these functions and only check for error once.

In example.h:

int error_flag = 0;  /* Set to non-zero value on error. */

double division(double n, double d);

In example.c:

#include "example.h"

double division(double n, double d)
{
    if (0 == d)
    {
        error_flag = 1;
        return 0.0;
    } else
        return n/d;
}

In main.c:

#include <stdio.h>
#include "example.h"

int main(int argc, char *argv[])
{
    double x;

    error_flag = 0; /* External variable */

    x = division(division(3.14, 1.3 - division(3.9, -3.0)), 7);
    if (error_flag)
    {
        /* The last call to division() didn't unset error_flag. */
        fprintf(stderr, "Something went wrong.\n");
        return 1;
    }

    /* Not reached. */
    printf("%f\n", x);
    return 0;
}

Math domain errors can be avoided

Sometimes.

Or don't do anything to handle math errors. If you try to open a file, it's difficult to predict the outcome, because knowing just the filename isn't enough; you'll have to check whether or not the file even exists and if it does exist you'll have to check the permissions. Math (as I know it) isn't that difficult, you only need to know the arguments. Consider `f(x) = 1/x`: you only need to know the value of `x` to determine if the call to `f` will fail or not, and this well known function is well known to be defined for `x != 0`. One liner: `double division(double n, double d) {return n/d;}`

(Proved myself wrong about that. Ex: f(a, b) = 1/(a+b))

Community
  • 1
  • 1
Oskar Skog
  • 332
  • 5
  • 16
  • Thank you Oskar, This is very good information and provides multiple ways of handling the issue. – Katianie Oct 21 '16 at 16:06
  • 1
    Note that no library function in the C standard or in the POSIX standard is allowed to set `errno = 0` (at least, no so that the calling code can detect it). You may legitimately decide that your code is going to break that rule, but you shouldn't do it by accident. You might use: `int old_errno = errno; errno.= 0; …active code setting result…; if (errno == 0) errno = old_errno; return result;`. – Jonathan Leffler Oct 22 '16 at 00:50
0

If you're looking for the least likely integer constant to hit by accident, use INT_MIN. It's one larger in magnitude than INT_MAX. It also has the advantage of being copyable to float without losing its value, INT_MAX will get rounded to a different value.

Of course this is only if you need compatibility with C. In C++ you really should be using exceptions.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 2
    `INT_MIN` is not guaranteed to be less than `-INT_MAX`. That is not guaranteed by the standard and only true for full-range of 2's complement. – too honest for this site Oct 21 '16 at 13:59
  • I like this aproach but like Olaf said, the values arent gaurenteed. – Katianie Oct 21 '16 at 14:02
  • @Olaf it's not guaranteed, but it's extremely likely just because two's complement is so wide-spread. And even if it's not, it doesn't hurt my assertion that this is the least likely integer. – Mark Ransom Oct 21 '16 at 14:02
  • @MarkRansom: Why is `-INT_MAX` less likely than `INT_MAX`? Said that: `float` might **not** be able to represent `INT_MIN` correctly (it is not for typical 32 or 64 bit systems). `double` can, but for both they can be norrmal results (unless you skip that particular value). – too honest for this site Oct 21 '16 at 14:33
  • 1
    @Olaf positive numbers are just more likely to be encountered in real life than negative numbers. And again you're correct, `INT_MIN` isn't *guaranteed* to fit in a float without rounding, but for 99.9% of the people seeing this answer it will be the case. – Mark Ransom Oct 21 '16 at 14:38
  • "positive numbers are just more likely to be encountered in real life than negative numbers" - Depends on the problem. There are problems which mostly have a negative result. "but for 99.9% of the people seeing this answer it will be the case" - Well, considering most systems in the field use 16 bit `int`, this is true indeed. But those systems don't use floating point types, just because they are not required and the MCUs have no FPU -> miserable performance. Thus the lib is likely targeted at 32 and 64 bit systems, for which this is **not** true -> problems pre-programmed! – too honest for this site Oct 21 '16 at 14:50
-1

The standard library used a variable called errno for explicitly this purpose. You could implement something similar.

For example, in your source file:

int matherr;

enum {
    SUCCESS,
    ZERO_DIVIDE,
    TOO_LARGE,
    ...
};

int multiply(int a, int b)
{
    matherr = SUCCESS;
    if (/* result too large*/) {
        matherr = TOO_LARGE;
        return 0;
    } else {
        return a*b;
    }
}

int divide(int a, int b)
{
    matherr = SUCCESS;
    if (/* result too large*/) {
        matherr = ZERO_DIVIDE;
        return 0;
    } else {
        return a/b;
    }
}

In your header file:

extern int matherr;

int multiply(int a, int b);
int divide(int a, int b);

In the code that calls these functions, it would then need to check the value of matherr if either function returns 0.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • 1
    But 0 is a valid result of multiplication, It would make more sense to return INT_MAX since the chances of a multiplcation equal that exactly are slim – Katianie Oct 21 '16 at 13:39
  • 1
    I'd suggest a change of the signature to be ResultEnum multiply(int a, int b, int* result) – UKMonkey Oct 21 '16 at 13:40
  • Why not just return INT_MAX? Prevents confusion to users of the library – Katianie Oct 21 '16 at 13:41
  • @Katianie Not really. The library just needs to document what each function returns in the event of an error. – dbush Oct 21 '16 at 13:43
  • @Katianie in case multiplication returns 0 as valid result, the `matherr` will contain SUCCESS, so the calling code can use the 0 as valid result. Looks to me as good value, as INT_MAX, in context of `matherr` solution, handling of error will look almost identically, but testing for 0 may be shorter or even faster than testing for INT_MAX. – Ped7g Oct 21 '16 at 13:46
  • How about multithreading? – Slava Oct 21 '16 at 13:57
  • @Katianie: Because `INT_MAX` could also be a valid result. Your question is simply too broad. If you have an unused code, use that, otherwise it depends. – too honest for this site Oct 21 '16 at 13:58
  • Another problem is that you have to check for error after every call, which could be too verbose especially for math operations. – Slava Oct 21 '16 at 14:02
  • Using a global variable like this indeed makes the library thread-unsafe. The `errno` set by the standard library is thread-local for this reason. – dpi Oct 21 '16 at 15:48