views:

509

answers:

5

I'm converting some 32-bit compatible code into 64-bit - and I've hit a snag. I'm compiling a VS2008 x64 project, and I receive this warning:

warning C4334: '<<' : result of 32-bit shift implicitly converted to 64 bits
(was 64-bit shift intended?)

Here's the original line of code:

if ((j & (1 << k)) != 0) {

And here's what it looks like if I follow Microsoft's advice:

if ((j & (1i64 << k)) != 0) {

Is this safe to do, when the code will be compiled on both 32-bit and 64-bit systems? If so, please explain why I must add "i64" to the end, and why this will not affect a 32-bit compilation. Otherwise, a work-around would be much appreciated.

Beyond this, I have what looks like an even trickier bit of code.

if (id[j] != id[j ^ (1u << k)]) {

I understand that the "u" means that the digit is unsigned, but what's the point in specifying that on a value that doesn't exceed the signed maximum value... I'm guessing this has something to do with the bit shift?

A: 

This is an integral literal of type __int64, having a numeric value of 1. Similar to how 1u is a literal of type unsigned, and 1L is a literal of type long.

adatapost
+1  A: 

1i64 I believe should be a signed, 64-bit integer. I can't proclaim any expertise in Microsoft's implementation, but in GCC, the solution for supporting 64-bit integers on 32-bit CPUs was to make long longs double-words using structs and various black magic macros. Therefore, i64 should be compatible.

As to the last bit of voodoo -- the only point in specifying 1u it is because it's possible that if k is large enough, the result of the shift would meet / exceed 32-bits of storage, in which case the outcome will be different if the LH operand is treated as a signed or an unsigned integer.

John Ewart
+5  A: 

1 has type int according to C++ Standard. On 64-bit Microsoft compiler int has sizeof = 4 bytes, it means that int is 32-bit variable. 1i64 has type __int64.

When you use shift operator the type of the result is the same as the type of the left operand. It means that shifting 1 you'll get 32-bit result. Microsoft compiler assumes that it could be not what you are expecting (on 64-bit platform) and gives you warning message.

When you use 1i64 result will be 64-bit on both platforms. j and 0 will be implicitly casted to 64-bit. So the whole expression will be calculated in 64-bit variables and result will be bool.

So using 1i64 is safe on both (32/64) platforms.

Kirill V. Lyadvinsky
It not only assumes so, it deduces the need of a 64 bit conversion from the second argument of the "and" operator. size_t is, indeed, 64 bit on a 64 bit architecture.
xtofl
Surely it deduces. The fact that it is `size_t` became public only three hours ago :)
Kirill V. Lyadvinsky
1i64 is only safe on MSVC platforms - it is not supported by other compilers or by the standard.
Jonathan Leffler
A: 

Since that code is 'and'ing the 64-bit variable j with the (32-bit) result of a bit-shift, the result will be 'expanded' to 64 bit by the compiler.

You probably want to be in control of how the second operand to the 'and' is calculated, so the compiler suggests you use the full 64 bits by making the first operand an __int64. This is safe in 32 bit, but you should actually look at the type of j to decide on the operator being 32 or 64 bit.

That's especially important in the second bit, where the result is used as an index.

xtofl
+1  A: 

The i64 suffix is Microsoft-specific. To be more portable (if you're worried about that), you might use the INT64_C() macro from stdint.h:

#include <stdint.h>

// ...

if ((j & (INT64_C( 1) << k)) != 0) { ... }

Unfortunately, MS doesn't have stdint.h as part of their C library (most other compilers seem to have it), but fortunately you can get one from several sources:

Now you'll have a 64-bit constant that'll work with a wide variety of compilers.

As far as why you might want or need the 64-bit value, it depends on the types of the various parts of the expression. It would be helpful to know the types of id, j, and k to be able to answer whether you need the the 'u' suffix on the constant or not, and what effect it might have.

Michael Burr
as far as I know, that header is part of C99, which means it is *not* part of C++. So it is no more portable than Microsoft's extension.
jalf
Michael Burr
Further, INT64_C() isn't a macro in the standard version of `<stdint.h>`, also rendering it not portable.
Jonathan Leffler
@Jonathan - are you sure? Am I misunderstanding 7.18.4.1: "The macro INTN_C(value) shall expand to a signed integer constant with the specified value and type int_leastN_t." Note: the N specifies the width, and 64 is required (by 7.18.1.2).
Michael Burr
Also, I added a qualification to 'portable' in the answer, which should make it a bit more accurate, even if it's still not acceptable :)
Michael Burr