0

I attempted to write a function that increments an unsigned integer and checks if the result overflowed, by comparing the old value of the integer with the value after incrementing:

unsigned detect_overflow(unsigned x) {
    if (x > ++x) {
        std::cerr << "Overflow occurred: " << x - 1 << " -> " << x << std::endl;
    }
    return x;
}

However, it doesn't work when the comparison in the if is written this way. Calling the function with UINT_MAX leads to undetected overflow.

But if the condition in the if is changed to use post-increment instead:

unsigned detect_overflow(unsigned x) {
    if (x++ > x) {
        std::cerr << "Overflow occurred: " << x - 1 << " -> " << x << std::endl;
    }
    return x;
}

then overflow is detected correctly:

Overflow occurred: 4294967295 -> 0

The same is true for the equivalent version of said function for detecting underflow.

What gives? I understand the difference between the value returned by post and pre-increment operators, but clearly my assumptions about how these values may be used in expressions containing side-effects are incorrect. What is going on here?

saxbophone
  • 779
  • 1
  • 6
  • 22
  • 1
    Is > a sequence point or is x > x++ the undefined behavior? – 273K Feb 03 '23 at 15:12
  • 5
    Your second bit of bad news is that both pieces of code have undefined behavior as the is no sequence point between your modification of `x` and your reading of its value. – NathanOliver Feb 03 '23 at 15:12
  • 1
    @273K The relational operators do not introduce a sequence point. – NathanOliver Feb 03 '23 at 15:12
  • why do you not compare to `x+1`? Are you worried about carrying out `x+1` once too often? (and even if its that, it doesnt require post or preincrement to avoid) – 463035818_is_not_an_ai Feb 03 '23 at 15:13
  • 4
    FWIW you can just check for `if (x < std::numeric_limits::max())` – NathanOliver Feb 03 '23 at 15:14
  • Everyone asking about using the more straightforward ways to detect overflow, yes I'm aware of them, the question is not about how to detect overflow, it is about why this approach that attempts to do too many things at once fails. From the comments, it's clear I am having sequencing issues. I wonder why they present in one example but not the other? I guess all bets _are_ off when it comes to UB... – saxbophone Feb 03 '23 at 15:16
  • 1
    yes, both example have the issue, but UB can appear to work – 463035818_is_not_an_ai Feb 03 '23 at 15:18
  • 1
    Yes, once you have UB anything is possible. It's not really meaningful to understand the behavior of UB as different implementations can/will give you different results with the same UB. – NathanOliver Feb 03 '23 at 15:19
  • Thanks to whoever linked the related questions, these weren't picked up during the _similar questions_ review at this question's creation, I appreciate it. – saxbophone Feb 03 '23 at 15:19
  • 1
    I guess you are using gcc: https://godbolt.org/z/qz81Tdbac ;) – Bob__ Feb 03 '23 at 15:20
  • @Bob__ how did you know‽ :D – saxbophone Feb 03 '23 at 15:21
  • So in other words, the answer is _"because the `>` operator does not introduce a sequence point, comparing `x` with itself in this way is unsequenced and hence, UB"_ – saxbophone Feb 03 '23 at 15:31

0 Answers0