Ошибка компилятора битового сдвига или угловой случай? - PullRequest
5 голосов
/ 24 ноября 2011

Следующий код выводит 0,1,32,33. Что нелогично, если не сказать больше. Но если я заменю литерал 1 константой типа «ОДИН», цикл будет работать нормально.

Это с gcc 4.6.2 и -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 Вопрос: Как отметил Бен, по умолчанию 1 равен 32-битной ширине. Почему это не повышается до 64 бит, когда его кооператив 64-битный.

РЕШЕНИЕ

Нет. << не требует, чтобы каждая сторона имела одинаковый тип. В конце концов, зачем делать правую сторону int64_t, когда максимальный доступный сдвиг вписывается в символ? Продвижение происходит только тогда, когда вы имеете дело с арифметическими операторами, а не со всеми операторами. </p>

Скопировано из комментариев Билла ниже

1 Ответ

7 голосов
/ 24 ноября 2011

Это проблема: (1<<k).

1 является интегральным литералом, который вписывается в int.

Если int имеет менее 64 бит на вашей платформе, то (1<<k) будет иметь неопределенное поведение к концу цикла, когда k велико. В вашем случае компилятор использует инструкцию Intel по сдвигу битов, и неопределенное поведение проявляется в том, как Intel определяет сдвиги, превышающие размер операнда - старшие биты игнорируются.

Вы, вероятно, хотите (1LL<<k)


Что говорится в стандарте (раздел 5.8 expr.shift):

Операнды должны быть целочисленного или незаданного типа перечисления, и выполняются интегральные преобразования. Тип результата - тип повышенного левого операнда. Поведение не определено, если правый операнд отрицательный или больше или равен длине в битах повышенного левого операнда.

Это в отличие от формулировки "Обычные арифметические преобразования выполняются для операнды арифметического или перечислимого типа ". которые присутствуют, например, для операторов сложения и вычитания.

Этот язык не изменился между C ++ 03 и C ++ 11.

...