21

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?

lavinio
  • 23,931
  • 5
  • 55
  • 71
Nick Bolton
  • 38,276
  • 70
  • 174
  • 242
  • 2
    What are the types of j and k? – bk1e Aug 12 '09 at 03:46
  • so what would be the right *multiplatform* answer? Is possible to change the current answer? – sorin Sep 15 '09 at 18:22
  • Which answer do you think is best? – Nick Bolton Sep 17 '09 at 19:30
  • 1
    I would change `1` to `static_cast(1)`: 1) this removes the warning. 2) the continues to behave in exactly the same way as it did before and as expected (at least on MSVC 32bit and 64bit). 3) the code behaves as it should. 4) it's cross-plattform C++ – Michael Jul 10 '18 at 12:44

4 Answers4

11

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
  • 97,037
  • 24
  • 136
  • 212
4

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
  • 333,147
  • 50
  • 533
  • 760
  • 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 Aug 12 '09 at 12:08
  • I guess you're right. Still it feels more portable to me. Maybe one could think of it as your own set of macros & types that aid in portability (kind of like the `U8`, `U32` or `INT16` macros you often see, especially in embedded programs), except that they're in more widespread, uniform use. Or like POSIX types which aren't part of the C/C++ standard, but are still often portable, even to Windows compilers. If it's any help, stdint.h is going to be in C++0x. – Michael Burr Aug 12 '09 at 14:20
  • Further, INT64_C() isn't a macro in the standard version of ``, also rendering it not portable. – Jonathan Leffler Aug 12 '09 at 21:47
  • @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 Aug 12 '09 at 22:17
  • 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 Aug 12 '09 at 22:19
  • You can also cast the 1 to 64-bit. Like this: `(uint64_t)1 << k` – Björn Lindqvist Jan 22 '18 at 17:31
1

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
  • 1,240
  • 1
  • 9
  • 6
0

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
  • 40,723
  • 12
  • 105
  • 192