9

I want signed integers to overflow when they become too big. How do I achieve that without using the next biggest datatype (or when I am already at int128_t)?

For example using 8bit integers 19*12 is commonly 260, but I want the result 1 11 10 01 00 with the 9th bit cut off, thus -27.

Henry B.
  • 91
  • 1
  • 2

7 Answers7

7

Signed overflow is undefined in C, and that's for real.

One solution follows:

signed_result = (unsigned int)one_argument + (unsigned int)other_argument;

The above solution involves implementation-defined behavior in the final conversion from unsigned to int but do not invoke undefined behavior. With most compilation platforms' implementation-defined choices, the result is exactly the two's complement result that you expect.

Finally, an optimizing compiler for one of the numerous platforms on which implementation-defined choices force the compiler to give you the behavior you expect will compile the above code to the obvious assembly instruction.

Alternately, if you are using gcc, then the options -fwrapv/-fno-strict-overflow may be exactly what you want. They provide an additional guarantee with respect to the standard that signed overflows wrap around. I'm not sure about the difference between the two.

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • The second paragraph to this answer is the closest thing to a correct answer to this question. That's the way I'd go. – R.. GitHub STOP HELPING ICE Nov 22 '10 at 00:45
  • Your solution of `signed_result = unsigned_expression` is not well-defined because of the following phrase in the standard: "if the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised" Section 6.3.1.3 of the C11 standard. – wich Apr 01 '16 at 12:40
  • @wich I see that the sentence “The above solution involves implementation-defined behavior.” included in my answer is confusing, at least to some readers. How do you think it should be better phrased? – Pascal Cuoq Apr 01 '16 at 20:29
  • Firstly I wouldn't call it a solution, at best it is a non-portable non-guaranteed workaround. As for the phrasing of the sentence you quoted, perhaps something like "The above will only do what you want when the compiler implements signed to unsigned int conversion the way you want it to be, as this behaviour is not defined in the standard." That would lead nicely into your following sentence, though also there you may want to separate between what is undefined, (unsigned to signed conversion,) and what is not, (everything else.) – wich Apr 02 '16 at 18:38
  • I see the somewhat awkward phrasing of the second sentence is a leftover of the previous version of your answer, as it stands it reads like the whole process is implementation-defined instead of just the unsigned to signed conversion. I'd remove the colon, and make clear the bit after the colon is an explanation of what the code is supposed to do. – wich Apr 02 '16 at 18:41
  • With [-fwrapv](https://gcc.gnu.org/onlinedocs/gcc-5.3.0/gcc/Code-Gen-Options.html), GCC will not guarantee that the signed overflow will wrap around, it will only assume it does, isn't it ? – Bilow Sep 08 '17 at 13:27
  • I don't think the example of it being UB "for real" is a particularly good one, since it merely shows that integer computations which overflow don't always yield consistent values. A better example would showcase the fact that, at least on gcc, side effects from integer overflow are not limited by ordinary laws of causality. – supercat Jun 02 '21 at 19:01
2

Signed integer overflow is undefined according to both C and C++ standards. There's no way to accomplish what you want without a specific platform in mind.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
2

It is possible to do this in a correct standard C manner, so long as you have access to an unsigned type that is of the same width as your signed type (that is, has one more value bit). To demonstrate with int64_t:

int64_t mult_wrap_2scomp(int64_t a, int64_t b)
{
    uint64_t result = (uint64_t)a * (uint64_t)b;

    if (result > INT64_MAX)
        return (int64_t)(result - INT64_MAX - 1) - INT64_MAX - 1;
    else
        return (int64_t)result;
}

This does not produce any problematic intermediate results.

caf
  • 233,326
  • 40
  • 323
  • 462
1

You could create an objective wrapper around int, but that would involve quite a lot of overhead code.

Šimon Tóth
  • 35,456
  • 20
  • 106
  • 151
1

Assuming two's complement signed integer arithmetic (which is a reasonable assumption these days), for addition and subtraction, just cast to unsigned to do the calculation. For multiplication and division, ensure the operands are positive, cast to unsigned, calculate and adjust the signs.

jilles
  • 10,509
  • 2
  • 26
  • 39
0

It sounds like you want to do unsinged integer arithmetic, then stuff the result into a signed integer:

unsigned char a = 19;
unsigned char b = 12;

signed char c = (signed char)(a*b);

should give you what you're looking for. Let us know if it doesn't.

Lee
  • 13,462
  • 1
  • 32
  • 45
  • That code still exhibits undefined behavior. (Still a signed overflow) – Billy ONeal Nov 21 '10 at 22:55
  • @Billy - not disagreeing with you at all... but I have seen a substantial amount of C code (usually realtime/embedded stuff) that relies on signed-subtraction of unsigned integer counter values, in order to obtain a "rolling delta". I thought that the signed-to-unsigned cast was well-defined, while an *overflow* was not. Just out of curiosity, can you provide a reference to the relevant part of the C standard that declares all this stuff to be "undefined behavior"? – Lee Nov 21 '10 at 23:16
  • @Lee: 1. I did not downvote this. 2. I don't see how that cast is not an overflow. I don't have a specific reference saying that the cast is invalid, but I don't have a specific reference saying that it's valid either. – Billy ONeal Nov 21 '10 at 23:29
  • 1
    It's not undefined, it's implementation-defined. A cast or implicit conversion to a signed type that's too small to hold the value is not considered an overflow by the standard. Rather an implementation-defined conversion is performed or an implementation-defined signal is raised. – R.. GitHub STOP HELPING ICE Nov 22 '10 at 00:43
  • And if you want to avoid the implementation-definedness, try this: `signed char c; *(unsigned char *)&c = (a*b);` – R.. GitHub STOP HELPING ICE Nov 22 '10 at 00:44
  • @R, @Billy - Thanks for the info. Interesting stuff. Always nice to learn something new. – Lee Nov 22 '10 at 15:42
  • @R.. I am not getting how implicit conversion is implementation-defined. Isn't `signed char ch = -200;` signed integer overflow? Aren't we implicitly casting `-200` to `signed char` type? – ajay Mar 02 '14 at 07:26
  • It's implementation-defined because the C language says it's implementation-defined. – R.. GitHub STOP HELPING ICE Mar 02 '14 at 18:05
-2

Use bigger datatypes. With GMP you will have all the space you probably need.

Milan
  • 15,389
  • 20
  • 57
  • 65
  • Question specifically asked how to do it without larger datatypes. And GMP is always a bad answer since it likes to `abort()` the calling program without warning. – R.. GitHub STOP HELPING ICE Nov 22 '10 at 00:45
  • The way I understood "(or when I am already at int128_t)?" is that he is willing to use larger datatypes if he can. GMP aborts when too much memory is requested. – Milan Nov 22 '10 at 01:51