Переключение заданного диапазона битов unsigned int в C


Я пытаюсь заменить следующий фрагмент кода

// code version 1
unsigned int time_stx = 11; // given range start
unsigned int time_enx = 19; // given range end
unsigned int time     = 0;  // desired output

while(time_stx < time_enx) time |= (1 << time_stx++);

Со следующим без петли

// code version 2
unsigned int time_stx = 11;
unsigned int time_enx = 19;
unsigned int time     = (1 << time_enx) - (1 << time_stx);

Получается, что в коде версии 1, time = 522240; в коде версии 2, time = 0; Когда я использую

printf("%un", time);

Чтобы сравнить результат. Я хотел бы знать, почему это происходит и есть ли более быстрый способ переключения битов в заданном диапазоне. Мой компилятор-gcc (Debian 4.9.2-10) 4.9.2.

Правка:

Спасибо за ответы. Я совершил глупую ошибку, и я ... чувствую себя неловко, отправляя свой вопрос без дальнейшего изучения моих кодов. Я сделал

unsigned int time_stx = 11;
unsigned int time_enx = 19;

unsigned int time1    = 0;
while(time_stx < time_enx) time1 |= (1 << time_stx++); // version 1

//// what I should, but forgotten to do
// time_stx = 11;
// time_enx = 19;

// where time_stx = time_enx now...
unsigned int time2    = (1 << time_enx) - (1 << time_stx); // version 2

// then obviously
printf("time1 = %un", time1); // time1 = 522240
printf("time2 = %un", time2); // time2 = 0
Я приношу свои извинения за причиненные неудобства. Замечание: и time_stx, и time_enx генерируются во время выполнения и не являются фиксированными.

Как предположили, что я допустил ошибку, и теперь проблема решена. Спасибо!!

1 3

1 ответ:

Читать битные твид-хаки. Даже если ответа там нет, вы будете лучше образованы в битвах. Кроме того, исходный код просто устанавливает биты в диапазоне; переключение означает превращение 1 бита в 0 бит и наоборот (обычно достигается с помощью ^ или xor).

Что касается кода, то я преобразовал три варианта выражения в следующий код C:

#include <stdio.h>

static void print(unsigned int v)
{
    printf("0x%.8X = %u\n", v, v);
}

static void bit_setter1(void)
{
    unsigned int time_stx = 11; // given range start
    unsigned int time_enx = 19; // given range end
    unsigned int time     = 0;  // desired output

    while (time_stx < time_enx)
        time |= (1 << time_stx++);

    print(time);
}

static void bit_setter2(void)
{
    unsigned int time_stx = 11;
    unsigned int time_enx = 19;
    unsigned int time     = (1 << time_enx) - (1 << time_stx);
    print(time);
}

static void bit_setter3(void)
{
    unsigned int time = 0xFF << 11;
    print(time);
}

int main(void)
{
    bit_setter1();
    bit_setter2();
    bit_setter3();
    return 0;
}

Когда я смотрю на ассемблер для него (GCC 5.1.0 на Mac OS X 10.10.3), я получить:

        .globl _main
_main:
LFB5:
LM1:
LVL0:
        subq    $8, %rsp
LCFI0:
LBB28:
LBB29:
LBB30:
LBB31:
LM2:
        movl    $522240, %edx
        movl    $522240, %esi
        leaq    LC0(%rip), %rdi
        xorl    %eax, %eax
        call    _printf
LVL1:
LBE31:
LBE30:
LBE29:
LBE28:
LBB32:
LBB33:
LBB34:
LBB35:
        movl    $522240, %edx
        movl    $522240, %esi
        xorl    %eax, %eax
        leaq    LC0(%rip), %rdi
        call    _printf
LVL2:
LBE35:
LBE34:
LBE33:
LBE32:
LBB36:
LBB37:
LBB38:
LBB39:
        movl    $522240, %edx
        movl    $522240, %esi
        xorl    %eax, %eax
        leaq    LC0(%rip), %rdi
        call    _printf
LVL3:
LBE39:
LBE38:
LBE37:
LBE36:
LM3:
        xorl    %eax, %eax
        addq    $8, %rsp
LCFI1:
        ret
Это удивительно большая коллекция этикеток! Компилятор полностью оценил все три минимальные функции bit_setterN() и встроил их вместе с вызовом print в тело main(). Это включает в себя оценку выражений до 522240 каждый раз. Компиляторы хороши в оптимизации. Напишите четкий код и дайте им это сделать, и они оптимизируют лучше, чем вы. Ясно, что если 11 и 19 не зафиксированы в вашем коде (они своего рода вычисляемые переменные, которые могут изменяться во время выполнения), то предварительное вычисление не так просто (и bit_setter3() не является стартером). Тогда код без цикла будет работать нормально, как и код цикла.

Для записи вывод будет следующим:

0x0007F800 = 522240
0x0007F800 = 522240
0x0007F800 = 522240

Если ваш компилятор Debian дает вам ноль от одного из фрагментов кода, то есть либо разница между тем, что вы скомпилировали, и тем, что вы опубликовали, либо ошибка в компиляторе. В целом, и никакого неуважения не предполагается, более вероятно, что вы допустили ошибку, чем то, что компилятор имеет ошибку в нем, которая появляется в коде так просто, как это.