5

I'm trying to make use of errno to detect if I've performed an operation that causes overflow. However, although I've written a function which deliberately overflows, errno == ERANGE is false. What's going on here?

Here's the code:

#include <stdio.h>
#include <errno.h>

int main(int argc, char* argv[]) {
    unsigned char c = 0;
    int i;

    for (i = 0; i< 300; i++) {
        errno = 0;
        c = c + 1;
        if (errno == ERANGE) {// we have a range error
            printf("Overflow. c = %u\n", c);
        } else {
            printf("No error. c = %u\n", c);
        }
    }
    return 0;
}

I expected this to give an overflow error at the point where we added one to 255, but there is no error. Here's the (truncated) output:

No error. c = 245
No error. c = 246
No error. c = 247
No error. c = 248
No error. c = 249
No error. c = 250
No error. c = 251
No error. c = 252
No error. c = 253
No error. c = 254
No error. c = 255
No error. c = 0
No error. c = 1
No error. c = 2
No error. c = 3
No error. c = 4
No error. c = 5
No error. c = 6
No error. c = 7
No error. c = 8
No error. c = 9

Can someone explain why it doesn't detect the error, and how I might change it or otherwise make a function that can detect if my value has overflowed? Note: ultimately I want to be doing this with long ints, so it's not possible to simply convert it to a larger data type.

EDIT:

I've since found some simple functions to detect overflow from addition and multiplication of integral data types, respectively. It doesn't cover all situations, but it covers a lot of them.

Multiplication:

int multOK(long x, long y)
/* Returns 1 if x and y can multiply without overflow, 0 otherwise */
{
    long p = x*y;

    return !x || p/x == y;
}

Signed addition:

int addOK(long x, long y)
/* Returns 1 if x and y can add without overflow, 0 otherwise */
{
    long sum = x+y;
    int neg_over = (x < 0) && (y < 0) && (sum >= 0);
    int pos_over = (x >= 0) && (y >= 0) && (sum < 0);
    return !neg_over && !pos_over;
}

Unsigned addition:

int unsignedAddOK(unsigned long x, unsigned long y)
/* Returns 1 if x and y can add without overflow, 0 otherwise */
{
    unsigned long sum = x+y;

    return sum > x && sum > y;
}
limp_chimp
  • 13,475
  • 17
  • 66
  • 105
  • Simply incrementing will not throw an error, and thus errno will not be set. *Functions* (eg. `strtod`) set errno. – jedwards Oct 13 '12 at 01:13
  • Also, looking into `` might be helpful, along with [this related question](http://stackoverflow.com/questions/199333/best-way-to-detect-integer-overflow-in-c-c). – jedwards Oct 13 '12 at 01:16
  • You may wish to use a language which checks for integer overflow (such as Ada or C# with `checked`) or one which transparently supports arbitrary precision arithmetic (such as Python, Lisp, Haskell, etc.) – Dietrich Epp Oct 13 '12 at 01:26

4 Answers4

2

Errno is not an processor-set variable, it is only used by system calls and some standard functions to repport errors.

I don't know any technique for detecting what you ask, exept from verifying if c < c + 1 on each loop iteration.

Edit : After some research, I found this wikipedia about status registers, which are CPU flags that indicates such errors.

tomahh
  • 13,441
  • 3
  • 49
  • 70
  • [This](http://stackoverflow.com/questions/12674969/why-was-no-intrinsic-access-to-the-cpus-status-register-in-the-design-of-both-c) post explains why there is no standard status register checking. – tomahh Oct 13 '12 at 01:21
2

From what I understand, errno is set by functions. It wouldn't detect addition overflow errors unless you create your own function that does. You can test to see if the result is less than one of your operands.

#include <stdio.h>
#include <errno.h>

int main(int argc, char* argv[]) {
    unsigned char c = 0, result = 0;
    int i;

    for (i = 0; i< 300; i++) {
        result = c + 1;
        if (result < c) {// we have a range error
            printf("Overflow. c = %u\n", result);
        } else {
            c = result;
            printf("No error. c = %u\n", c);
        }
    }
    return 0;
}
NullEntity
  • 152
  • 11
  • Yes, but that doesn't account for all possibilities. For example, if you add a value which itself exceeds the overflow limit, for example attempting to add 300 to a char, then depending on the original value of `c` you might still get a number which is greater than `c`, but nonetheless wrong. – limp_chimp Oct 13 '12 at 01:32
2

In addition to the point about errno made by others, you're using unsigned char for the counter. Unsigned types are explicitly defined to wrap around. In other words, it is not an error to compute c=255; c=c+1; when c is an 8-bit unsigned char: the result is always 0.

han
  • 755
  • 4
  • 7
1

Integer overflow is undefined behavior. Undefined behavior is not a detectable condition. It's endgame. You cannot draw any conclusions about the state of a program once you have invoked undefined behavior.

Aside from that, errno has nothing to do with arithmetic expressions. It's used to report errors from library functions.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711