-1
#include <limits>
#include <cstdint>
#include <iostream>

template<typename T>
T f(T const a = std::numeric_limits<T>::min(),
    T const b = std::numeric_limits<T>::max())
{
    if (a >= b)
    {
        throw 1;
    }

    auto n = static_cast<std::uint64_t>(b - a + 1);
    if (0 == n)
    {
        n = 1;
    }

    return n;
}

int main()
{
    std::cout << f<int>() << std::endl;
}

g++-11 -std=c++20 -O2 should output 0 other than 1!

clang++ is ok as expected. If I change -O2 to -O0, g++-11 is also ok.

See: online demo

Why does g++ -O2 contain a bug while -O0 is ok?

xmllmx
  • 39,765
  • 26
  • 162
  • 323
  • Why the C++11-tag when you compile with c++20? One of them is incorrect. – Lukas-T Jun 11 '21 at 13:22
  • Weird. If you just do `std::cout << (std::numeric_limits::max() - std::numeric_limits::min() + 1);` in GCC, it prints the value of `0`: https://godbolt.org/z/nn8fsnj8Y – NathanOliver Jun 11 '21 at 13:26
  • 3
    Isn't `b - a` signed overflow? – Timo Jun 11 '21 at 13:28
  • I wonder if it is some off by one error the optimizer has and it's compile time computation is 1 less then it should be. – NathanOliver Jun 11 '21 at 13:28
  • 2
    If `a` and `b` are `int`, with values `std::numeric_limits::min()` and `std::numeric_limits::max()` respectively, then `b - a + 1` is computed as an `int`, and the result of that calculation overflows an `int`, so causes undefined behaviour. So both compilers are correct, regardless of optimisation flags. – Peter Jun 11 '21 at 13:29
  • @Peter which is now well defined in C++20 – Richard Critten Jun 11 '21 at 13:30
  • I click your demo link and it show me that result is 0 like it should be. – Marek R Jun 11 '21 at 13:30
  • 3
    Well tags now changed again from C++20 to C++11 so we're in UB again :D – Timo Jun 11 '21 at 13:31
  • The reasons gcc contains bugs is because it is software. clang and other compilers also have their own bugs. Only the most trivial software is bug-free. – François Andrieux Jun 11 '21 at 13:32
  • 2
    @RichardCritten - My understanding is that C++20 requires twos-complement, but that overflow of signed integral types remains undefined behaviour. – Peter Jun 11 '21 at 13:35
  • @Peter You I think you are correct - trying to find a definitive quote – Richard Critten Jun 11 '21 at 13:37
  • Hmm and now we're back to C++20. OP trying to trick us. – Timo Jun 11 '21 at 13:37
  • 1
    Does this answer your question? [Ramifications of C++20 requiring two's complement](https://stackoverflow.com/questions/57363324/ramifications-of-c20-requiring-twos-complement) _"...This was explicitly considered and people felt that the best option was keeping it __undefined behavior__...."_ – Richard Critten Jun 11 '21 at 13:38
  • 2
    I wouldn't describe this as "definitive", but http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/n4878.pdf (the most recent C++20 draft according to https://github.com/cplusplus/draft/blob/master/papers/wd-index.md ) has a note in Section 6.8.2 after the second para stating "[Note 2: Unsigned arithmetic does not overflow. Overflow for signed arithmetic yields undefined behavior (7.1).— end note]". I haven't looked further (and I don't have a ratified version of the latest C++ standard handy at present) but it is pretty ... suggestive. – Peter Jun 11 '21 at 13:50

1 Answers1

5

b - a + 1 is clearly UB when the type of a and b are int and a is INT_MIN and b is INT_MAX as signed overflow is undefined behavior.

From cppreference:

When signed integer arithmetic operation overflows (the result does not fit in the result type), the behavior is undefined

You are not converting to int64_t until after the calculation has already been executed.

Mestkon
  • 3,532
  • 7
  • 18
  • C++20 mandates twos compliment for signed integers now – NathanOliver Jun 11 '21 at 13:31
  • Weird, https://en.cppreference.com/w/cpp/language/operator_arithmetic doesn't mention the new rules for c++20 – Mestkon Jun 11 '21 at 13:32
  • 3
    Even though c++20 enforces 2's complement signed integers, it does not state anything new about overflows in this context – Mestkon Jun 11 '21 at 13:35
  • @Mestkon Cppreference hasn't caught up yet. See the actual standard text here: https://timsong-cpp.github.io/cppwp/conv.integral#3 – NathanOliver Jun 11 '21 at 13:41
  • 3
    @NathanOliver That rule is about integer conversions. It doesn't apply to arithmetic overflow. – eerorika Jun 11 '21 at 13:41
  • @NathanOliver The rule that says overflow is UB is https://timsong-cpp.github.io/cppwp/expr.pre#4 That technically applies to all numeric types, so the rule excluding unsigned must be somewhere else. I found a non-normative note but not an actual rule https://timsong-cpp.github.io/cppwp/basic.fundamental#note-2. – eerorika Jun 11 '21 at 13:52
  • @eerorika Yeah, looks like I need to readjust how much that change actually did. – NathanOliver Jun 11 '21 at 13:53
  • @NathanOliver The change basically gives standard defined (as opposed to implementation defined) behaviour for conversions to smaller signed integer type as well as reinterpretation casts between signed and unsigned. And I guess the exact range of values for signed types depending on width. – eerorika Jun 11 '21 at 13:55
  • @NathanOliver it was deemed _too harmful_ to define signed overflow behavior, as it would disrupt existing compiler optimizations that assume signed overflow "never happens". – Drew Dormann Jun 11 '21 at 17:26