Warning C4146 minus operator on unsigned type

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

warning C4146: unary minus operator applied to unsigned type, result is 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 this by changing the library as little as possible?

+3


source to share


1 answer


Type v

- std::uint32_t

, which is an unsigned type. The unsigned type is commonly used for indexes and counters, since they can never be negative.

Attempting to reverse the sign of an unsigned number is usually suspicious, so the compiler gives a warning. In this case, however, it is safe and well-defined, and the library relies on the details of exactly what it means to flip the sign of an unsigned number.

From the C ++ 11 standard:

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

[Where the standard says 2 ^ n, it means literally, although 2 ^ n cannot be represented in an unsigned type of n bits. The most common way to do 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, you can try using translation here. (Casts should only be used rarely when you need to force the compiler to treat a value as something else, which is 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];

      

An internal cast asks the compiler to convert v

to a 32-bit signed integer. For values v

up to 2 ^ 31 - 1 this results in the same value. For large values, v

this will result in a negative value.

But now you flip the sign on the signed value (which the compiler will gladly do), but is no longer guaranteed by the standard to do the same. (All modern machines use two-way complement, so this will actually give the same result.)

If you want to be nit-picky (like me), you can perform bitwise operations directly on the unsigned value using the top two's complement trick. Instead -v

, you will have (~v + 1u)

:

r = MultiplyDeBruijnBitPosition[((uint32_t)((v & (~v + 1u)) * 0x077CB531U)) >> 27];

      

+5


source







All Articles