2009-08-20

Code Gotchas: Borderline integer


Sometimes one finds a weird bug which “shouldn’t happen” but still does. And after more or less tedious debugging session the bug is found, and analyzing it shows it’s probably quite logical why the thing happened after all.

Even if it may be stressful to try find such nasty problems, in the end I’m usually fascinated by such weird minute details. Here is one such finding.

For this one I’ll present just the pure problem without a “solution” first, which I’ll add later. Feel free to add a comment and describe what you think — this one is actually really obvious.

Suppose you have a function “doStuff(x);” where x should be absolute value of the original. That is, the value passed to doStuff must be zero or positive. So you could have e.g. code like this:


int x;

// ... fill x with something ...

// and then find the absolute value...
x = abs(x);
// or alternatively just: if (x < 0) x = -x; // ... // and then do the work: doStuff(x);

Even that code can fail horribly. But why and how?

EDIT: Added clarification that also zero is ok.

Answer:

Absolute value of smallest integer doesn't fit in same sized integer but wraps back to being the same value. For example: if we assume size of int is 32 bits then if you assign -2147483648 (-0x80000000) to x, and execute "x = -x;", you'll still get the same value as result. Like suggested in the comments, the fix is to put the result to an unsigned integer: unsigned int x2; x2 = (unsigned int)abs(x); doStuff(x2);. Of course this assumes that the doStuff takes in also unsigned int. I deliberately left that out from the description... :-)

Note that with this whole thing we're of course assuming that two's-complement system is being used. But that probably includes any modern platform you're ever going to use so it is a safe assumption.

I have another gotcha which is kind of a nice continuation to this one, as it concerns use of unsigned integers. I'll write about that maybe next week.

8 responses:

  1. philhassey says:

    maybe if x=0 then it fails? to fix, make it doStuff(max(0.001,abs(x))) ?

  2. philhassey says:

    take 2 .. say they are 16 bit integers ..

    abs(-32768) might turn into a 0 causing unexpected results?

  3. ion says:

    x = -0x80000000;

  4. fydo says:

    Just use an unsigned variable! Bam! :D

  5. atte says:

    abs(x) for MIN_INTEGER returns MIN_INTEGER, which works if the value is later assigned into “unsigned int”. So doStuff(x) function should use “unsigned int” as parameter instead of “int”. Most likely causes warnings or something :).

  6. jetro says:

    Thanks for the answers, you got it right. :) I updated the blog post to contain my description.

  7. atte says:

    Actually you will be fine if doStuff(x) just uses unsigned int and do the function call in this way:
    doStuff((unsigned int)x));

    where

    void doStuff(unsigned int x) { … }

    It should work without casting too, then you would just get a warning or something. There is no requirement of doing the casting right away.

  8. jetro says:

    Of course, although I consider it is just the same thing in any case — type cast, be it implicit or explicit. :)

    In actual situation I think it’s more likely that there’s some following code with the same requirement (x>=0) rather than just a function call… in that case it’s probably more clear to have the temp var.

Leave a Reply

CodeRSS feed for responses —Trackback link.


3rd party advertisement:
(not chosen by me, picked by Google for you)