0

while doing some coding, I got a rather strange behavior, which seems du to UB, but I'm asking in case I missed something obvious! I compile this code with gcc 9.3, and -O3, in debug mode there is no problems

#include <array>
#include <climits>
#include <iostream>

void integer_overflow(long long v)
{
   unsigned long long uv;
   // removing the following comments here make the function works!
   //   if (v == LONG_LONG_MIN)
   //      uv = static_cast<unsigned long long>(v);
   //   else
   uv = (v < 0) ? -v : v;

   std::array<char, 40> buffer;

   auto last = buffer.rbegin();
   *last++   = '\0';
   if (v == 0)
   {
      *last++ = '0';
   }
   else
   {
      auto digits = 0;

      do
      {
         auto rem = uv % 10ULL;
         *last++  = rem + '0';
         uv /= 10ULL;
         if (uv != 0 && ++digits == 3)
         {
            *last++ = ',';   // comment this line, the loop work!, but not the good output!!!
            digits  = 0;
         }
      } while (uv != 0);
      // take care of neg values!
      if (v < 0)
         *last++ = '-';
   }
   std::cout << last.base() << std::endl;
}


int main(int argc, char** argv)
{
   integer_overflow(LONG_LONG_MIN + 1);   // ok
   integer_overflow(LONG_LONG_MIN);       // does not work
}

Output:

-9,223,372,036,854,775,807

-8

so my question how do I convert LONG_LONG_MIN to it positive counterpart in an unsigned long long ?

also is that UB as I suspect ? I think it is UB because -v for the smallest integral value on intel two's complement is probably undefined!

  • Try `uv = static_cast(v); if (v < 0) uv = -uv;`, then the negation is performed on an unsigned data type which is well-defined. – Ben Voigt Apr 04 '22 at 19:51
  • You can ignore the sign until you get the digit. `bool is_negative = v < 0; auto rem = v % 10ULL; *last++ = (is_negative ? -rem : rem) + '0';` – KamilCuk Apr 04 '22 at 19:52

1 Answers1

0

Completing @KamilCuk 's answer, one way you could convert an integer to its absolute value in the unsigned equivalent type is by casting it then negating it if it used to be negative.

Negating/under/overflowing an unsigned integer is always fine.

int32_t x;
uint32_t absOfX = x<0 ? -((uint32_t)x) : x;

Edit: following the deletion of the other answer, I complete this one - negating the minimum value of a signed integer type is indeed UB (see Is negating INT_MIN undefined behaviour?)

Similarly, so is taking its absolute value using the standard library function. The reference for std::abs mentions:

The behavior is undefined if the result cannot be represented by the return type.

Julien BERNARD
  • 653
  • 6
  • 17