C shift right не работи правилно на типове int в моята програма

Имам следната функция в C:

int lrot32(int a, int n)
{
    printf("%X SHR %d = %X\n",a, 32-n, (a >> (32-n)));
    return ((a << n) | (a >> (32-n)));
}

Когато предам като аргументи lrot32(0x8F5AEB9C, 0xB) получавам следното:

8F5AEB9C shr 21 = FFFFFC7A

Резултатът обаче трябва да е 47А. какво правя грешно

Благодаря ви за отделеното време


person Marc    schedule 01.07.2017    source източник
comment
Имайте предвид, че заглавието на вашия въпрос предполага, че много програми не работят.   -  person Iharob Al Asimi    schedule 01.07.2017
comment
Опитвали ли сте да пъхнете 0x8F5AEB9C в int на вашата платформа?   -  person WhozCraig    schedule 01.07.2017
comment
Когато работите с битове и побитови операции, винаги използвайте неподписани типове.   -  person Some programmer dude    schedule 01.07.2017


Отговори (1)


int е тип цяло число със знак. C11 6.5.7p4-5 казва следното:

4 Резултатът от E1 << E2 е E1 изместени наляво E2 битови позиции; освободените битове се запълват с нули. [...] Ако E1 има тип със знак и неотрицателна стойност и E1 x 2E2 е представимо в типа резултат, тогава това е резултантната стойност; в противен случай поведението е недефинирано.

5 Резултатът от E1 >> E2 е E1 изместени надясно E2 битови позиции. [...] ако E1 има тип със знак и неотрицателна стойност, стойността на резултата е неразделна част от частното на E1 / 2E2. Ако E1 има тип със знак и отрицателна стойност, получената стойност е дефинирана от внедряването.

Така в случая на <<, ако изместената стойност е отрицателна или положителната стойност след изместване не може да бъде представена в типа резултат (тук: int), поведението е недефинирано; в случай на >>, ако стойността е отрицателна, резултатът е дефинирана реализация.

Така и в двата случая ще получите резултати, които най-малкото зависят от внедряването, а в случай на ляво преместване, по-лошо, вероятно от нивото на оптимизация и други подобни. Една стриктно съответстваща програма не може да разчита на конкретно поведение.


Ако все пак искате да се насочите към конкретен компилатор, тогава проверете неговите ръководства за това какво би било поведението - ако е посочено такова - би било. Например GCC казва :

Резултатите от някои побитови операции върху цели числа със знак (C90 6.3, C99 и C11 6.5).

Побитовите оператори действат върху представянето на стойността, включително битовете за знак и стойност, където битът за знак се счита непосредствено над бита с най-висока стойност. Signed ‘>>’ действа върху отрицателни числа чрез знаково разширение. [*]

Като разширение на езика C, GCC не използва ширината, дадена в C99 и C11, само за да третира определени аспекти на знака „‹‹“ като недефинирани. Въпреки това, -fsanitize=shift (и -fsanitize =недефинирано) ще диагностицира такива случаи. Те също се диагностицират, когато се изискват постоянни изрази.

[*] разширението на знака тук означава, че знаковият бит - който е 1 за отрицателни цели числа, се повтаря от сумата на смяна, когато се изпълнява десен Shift - това е причината да видите тези Fs в резултата.

Освен това GCC винаги изисква допълващо представяне на 2, така че ако винаги използвате GCC, без значение към коя архитектура сте се насочили, това е поведението, което ще видите. Освен това в бъдеще някой може да използва друг компилатор за вашия код, като по този начин причини друго поведение там.


Може би бихте искали да използвате цели числа без знак - unsigned int или по-скоро, ако се очаква определена ширина, тогава например uint32_t, тъй като смените винаги са добре дефинирани за него и изглежда отговарят на вашите очаквания.


Друго нещо, което трябва да се отбележи, е, че не всички смени са разрешени. C11 6.5.7 p3:

[...]Ако стойността на десния операнд е отрицателна или е по-голяма или равна на ширината на повишения ляв операнд, поведението е недефинирано.

Така, ако някога преместите цяло число без знак с ширина 32 бита на 32 - наляво или надясно, поведението е недефинирано. Това трябва да се има предвид. Дори компилаторът да не направи нищо шантаво, някои процесорни архитектури действат така, сякаш изместването с 32 би изместило всички битове - други се държат така, сякаш стойността на изместването е 0.

person Antti Haapala    schedule 01.07.2017
comment
Може да си струва да се отбележи, че при повечето от днешните реализации знаковият бит се премества върху >> - това е, което OP вижда. - person tofro; 01.07.2017
comment
uint32_t ще бъде по-добър тип, тъй като намерението изглежда е 32-битови операции - person M.M; 01.07.2017