5

The following code outputs 0,1,32,33. Which is counter intuitive to say the least. But if I replace the literal 1 with the type annonated constant "ONE", the loop runs fine.

This is with gcc 4.6.2 and -std=c++0x.

#include<iostream>
#include<cstdint>
using namespace std;
int main()
    {
    int64_t bitmask = 3;
    int64_t k;
    const int64_t ONE = 1;
    cout<<"bitmask = "<<bitmask<<endl;

    for(k=0; k<64; k++)
        {
        if(bitmask & (1<<k))
            {
            cout<<"k="<<k<<endl;
            }
        }

    return 0;
    } 

EDIT Question: As Ben pointed out, 1 is seen to be 32 bit wide by default. Why is it not promoted to 64 bits when it's co-operand is 64 bits.

SOLUTION

No. << does not require that each side have the same type. After all, why make the right side an int64_t when the maximum shift available fits in a char? The promotion only occurs when you are dealing with arithmetic operators, not all operators.

Copied from Bill's comments below

rpg
  • 975
  • 1
  • 8
  • 18
  • 1
    possible duplicate of [How do I bit shift a long by more than 32 bits?](http://stackoverflow.com/questions/2404439/how-do-i-bit-shift-a-long-by-more-than-32-bits) – Ben Voigt Nov 24 '11 at 06:29

1 Answers1

8

This is a problem: (1<<k).

1 is a integral literal that fits in an int.

If int has fewer than 64 bits on your platform, then (1<<k) will have undefined behavior toward the end of the loop, when k is large. In your case, the compiler is using an Intel bitshift instruction, and the undefined behavior comes out the way Intel defines shifts larger than the operand size -- the high bits are ignored.

You probably want (1LL<<k)


What the standard says (section 5.8 expr.shift):

The operands shall be of integral or unscoped enumeration type and integral promotions are performed. The type of the result is that of the promoted left operand. The behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand.

This in contrast to the wording "The usual arithmetic conversions are performed for operands of arithmetic or enumeration type." which is present for e.g. addition and subtraction operators.

This language didn't change between C++03 and C++11.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • But shouldn't 1 be promoted to int64_t as it's co-operand is 64 bit wide. If you try to add an int and a float, it would certainly promote the int to float. – rpg Nov 24 '11 at 06:29
  • 1
    @rpg: No. `<<` does not require that each side have the same type. After all, why make the right side an `int64_t` when the maximum shift available fits in a `char`? The promotion only occurs when you are dealing with arithmetic operators, not all operators. – Billy ONeal Nov 24 '11 at 06:31
  • @rpg: No. The operands have different functions (one is a value, the other is a count of bits). It wouldn't make sense to coerce both operands to the same type. And in fact, the standard calls for the left operand to be promoted independent of the type of the right operand. – Ben Voigt Nov 24 '11 at 06:31
  • @Ben: You should probably note that "integral promotions are performed" doesn't mean things can ever promote to `long long` ... – Billy ONeal Nov 24 '11 at 06:37
  • @BillyONeal: My copy of the standard mentions promotion to `long long int` (section 4.5, p2 and p3). But that won't happen for this code. – Ben Voigt Nov 24 '11 at 06:39
  • @BenVoigt: Yes, values can be converted to `long long int`. But IIRC those aren't referred to as "the intergal promotions". (Though honestly I don't have a copy of C++11 so I'm kind of interpolating between C99 and C++03 here) C99 says "If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.) All other types are unchanged by the integer promotions." – Billy ONeal Nov 24 '11 at 06:44
  • @BillyONeal: Section 4.5 IS "Integral Promotions". Specifically it calls out that `wchar_t` and enums might be too big to promote to `long int` and `unsigned long int`, in which case `long long int` and `unsigned long long int` will be considered. – Ben Voigt Nov 24 '11 at 06:48
  • @BenVoigt: Ah. I really need to get a copy of this damn standard. Too bad ISO charges so freaking much for it :/ – Billy ONeal Nov 24 '11 at 06:49
  • @BillyONeal: Didn't you get a copy of n3290 when it was freely available? – Ben Voigt Nov 24 '11 at 06:53
  • @Ben: Nope. Would have been nice :/ – Billy ONeal Nov 24 '11 at 06:56
  • @BillyONeal: So e-mail me. And promise to report any defects you find (those are the terms under which n3290 can be used and distributed). – Ben Voigt Nov 24 '11 at 17:46
  • I would use `INT64_C(1)< – ndim Aug 23 '17 at 20:20
  • 1
    @ndim: `INT64_C` might not be available on all platforms. `LL` suffix always works and is less wordy, so I prefer it. – Ben Voigt Aug 23 '17 at 20:30