Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

warning C4146 minus operator on unsigned type

I have this code from a library I want to use. On compiling, I get the following warning:

warning C4146: unary minus operator applied to unsigned type, result still unsigned

inline int lastbit (uint32_t v)
{
  int r;
  static const int MultiplyDeBruijnBitPosition[32] = 
    {
      0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 
      31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
    };
  r = MultiplyDeBruijnBitPosition[((uint32_t)((v & -v) * 0x077CB531U)) >> 27];
  return r;
}

How can I fix it with altering the library as little as possible?

like image 240
user1930254 Avatar asked Nov 12 '14 17:11

user1930254


1 Answers

Update 2021-05-18: An even better solution from Raymond Chen's blog:

neg_v = (0 - v)

The compiler doesn't warn because unsigned subtraction is well-defined even when the value on the right is larger than the value on the left. You could write 0u instead of 0, but I think the integral promotions will take care of that for you.

Original Answer Below

The type for v is std::uint32_t, which is an unsigned type. An unsigned type is typically used for indexes and counts, since they can never be negative.

Attempting to flip the sign on an unsigned number is generally suspicious, which is why the compiler gives a warning. In this case, however, it's safe and well-defined, and the library is relying on details of what exactly it means to flip the sign on an unsigned number.

From the C++11 standard:

The negative of an unsigned quantity is computed by subtracting its value from the 2^n, where n is the number of bits in the promoted operand. The type of the result is the type of the promoted operand. [Section 5.3.1.8]

[Where the standard says 2^n, it means that literally, even though 2^n can't be represented in an unsigned type of n bits. The most common way to implement this without using a larger type is to flip all the bits and then add one: neg_v = ~v + 1;.]

To convince the compiler that this operation is OK, here, you might try to use a cast. (Casts should be used only rarely, when you need to force the compiler to treat a value as something other that its natural type.)

const uint32_t neg_v = static_cast<uint32_t>(-static_cast<int32_t>(v));
r = MultiplyDeBruijnBitPosition[((uint32_t)((v & neg_v) * 0x077CB531U)) >> 27];

The inner cast asks the compiler to convert v to a 32-bit signed integer. For values of v up to 2^31 - 1, this results in the same value. For larger values of v, this will result in a negative value.

But now you're flipping the sign on a signed value (which the compiler will happily do), but it's no longer guaranteed by the standard to do exactly the same thing. (All modern machines use two's-complement, so in effect, it will give the same result.)

If you wanted to be nit-picky (like me), you could perform the bitwise operations directly on the unsigned value, using the two's-complement trick from above. Instead of -v, you'd have (~v + 1u):

r = MultiplyDeBruijnBitPosition[((uint32_t)((v & (~v + 1u)) * 0x077CB531U)) >> 27];
like image 92
Adrian McCarthy Avatar answered Oct 19 '22 05:10

Adrian McCarthy