Нечетный сдвиг битов приводит к C#

Учитывая, что у меня есть значение uint 2402914, и я хотел бы захватить крайние левые 17 бит, где ошибка в моей логике, выполнив этот код:

int testop = 0;
byte[] myArray = BitConverter.GetBytes(2402914);    
fixed (byte* p = &myArray[0])    
{   
    testop = *p >> 15;    
}

мой ожидаемый результат

50516.

person Jason    schedule 20.07.2011    source источник
comment
Какую ценность вы на самом деле получаете?   -  person FishBasketGordo    schedule 20.07.2011
comment
myArray это: [98, 170, 36, 0] *p это указатель на myArray[0] или (byte)98, который вы затем сдвигаете на 15 позиций, прямо из 8-битного диапазона. Я думаю. Я немного заржавел с битовыми манипуляциями.   -  person asawyer    schedule 20.07.2011
comment
Мне кажется, или это не C#, а C или C++? Кажется, я вижу там указатели. Если только я не упустил что-то очевидное.   -  person Andy West    schedule 20.07.2011
comment
@Andy Вы можете делать указатели в С#, просто они должны быть в контексте unsafe   -  person asawyer    schedule 20.07.2011
comment
@Энди, это можно сделать на C#, используя ключевое слово unsafe и переключатель /unsafe при сборке.   -  person Jon Egerton    schedule 20.07.2011
comment
@ФишБаски, @Джеймс. Я только что выполнил это и получил testop = 0   -  person Jon Egerton    schedule 20.07.2011
comment
Вау здорово. Извините, что засоряю этот пост своим невежеством. :) Не могу поверить, что я пропустил это.   -  person Andy West    schedule 20.07.2011
comment
Я запутался... 2402914 в битах равно 00000000001001001010101001100010, поэтому самое левое 17: 00000000001001001, что является десятичным числом 73. Как вы получаете 50516? А нельзя ли просто сделать testop = 2402914 >> 15?   -  person CodingWithSpike    schedule 20.07.2011
comment
что не так с (value) >> (32-17) ?   -  person Mark Segal    schedule 20.07.2011
comment
@Jon Да, 98 равно 01100010 Вам нужно всего лишь ›› 7, чтобы получить 0, у @Jason есть ›› 15   -  person asawyer    schedule 20.07.2011
comment
Здесь много Джейсонов стучит, это сбивает с толку в комментариях!   -  person Jon Egerton    schedule 20.07.2011


Ответы (4)


*p просто дает вам первый байт; это эквивалентно p[0]. Вам нужно будет использовать сдвиг и ИЛИ для объединения битов из первых трех байтов (или последних трех байтов, в зависимости от порядка байтов...)

Если этот код не является упрощенной версией чего-то более сложного, и вы на самом деле пытаетесь просто извлечь крайние левые 17 битов из int, это должно работать:

int testop = (someInt >> 15) & 0x1ffff;

(Редактировать: добавлено & 0x1ffff, чтобы оно работало и с отрицательными целыми числами; спасибо @James.)

person Aasmund Eldhuset    schedule 20.07.2011
comment
Осмунд, это то, что я придумал изначально. Хотя это сработало, я все же хотел понять, почему мое переключение передач не работает. Это имеет смысл, но я надеялся, что указатель был на первую запись в массиве и будет продолжаться дальше в памяти по мере того, как я переключаюсь. Ну что ж. - person Jason; 20.07.2011
comment
@Jason: В тот момент, когда вы разыменовываете указатель, вы получаете значение одного байта, и любые операции с ним могут видеть только этот байт, а не то, что находится рядом с ним. - person Aasmund Eldhuset; 20.07.2011
comment
@Jason: Но если вы хотите извлечь биты из int, не можете ли вы просто применить >> непосредственно к int? Смотрите мою правку. - person Aasmund Eldhuset; 20.07.2011
comment
Я не минусовал вас, но вам нужно следить за сохранением знака при сдвиге подписанных целых чисел вправо. - person James; 21.07.2011
comment
@James: Хороший вопрос; Я отредактировал код, чтобы он обрабатывал отрицательные числа. - person Aasmund Eldhuset; 21.07.2011

Возможно, вы захотите, чтобы ваши ожидания соответствовали реальности. Сдвиг вправо эквивалентен делению на 2. Фактически вы делите на 2 пятнадцать раз, что равнозначно тому, что вы делите на 2^15 = 32768. Обратите внимание, что 2402914 / 32768 = 73 (усекая остаток).

Поэтому я бы ожидал, что результат будет 73, а не 50516.

Фактически,

2402914_10 = 0000 0000 0010 0100 1010 1010 0110 0010_2

Таким образом, самые левые семнадцать битов

             0000 0000 0010 0100 1

Обратите внимание, что

0000 0000 0010 0100 1 = 1 * 1 + 0 * 2 + 0 * 4 + 1 * 8 + 0 * 16 + 0 * 32 + 1 * 64 
                      = 73

Обратите внимание, что вы можете получить этот результат проще с помощью

int testop = 2402914 >> 15;
person jason    schedule 20.07.2011
comment
Он не сдвигает целое число (хотя именно это он и намеревается сделать), он сдвигает байт. Результат становится нулевым. - person Aasmund Eldhuset; 20.07.2011
comment
@Aasmund Eldhuset: Что? Учитывая, что у меня есть значение uint 2402914, и я хотел бы захватить крайние левые 17 бит, где ошибка в моей логике, выполнив этот код, и мой ожидаемый результат равен 50516. Его ожидаемый результат, основанный на заданном им вопросе, неверен. . - person jason; 20.07.2011
comment
@ Джейсон - Аасмунд прав в моих намерениях. Это всего лишь пример мышления C++, пытающегося примениться в мире C#. - person Jason; 20.07.2011
comment
@Jason (задавший вопрос) Я был бы очень удивлен, если бы этот код работал так, как вы предполагали, в C/C++. Я ожидаю, что C++ даст здесь тот же результат, что и C#. Так какое отношение к этому имеет образ мышления C++? - person CodesInChaos; 20.07.2011
comment
@Jason: Это не имеет ничего общего с языковыми проблемами. Если вы возьмете 2402914 и захотите, чтобы его семнадцать старших битов были семнадцатью младшими битами некоторого целого числа, вы получите 73, а не 50516, как вы ожидаете. Чтобы получить этот результат, вы просто выполняете сдвиг на 15 бит. Вы можете сделать это на любом современном языке, производном от C by 2402914 ›› 15. - person jason; 20.07.2011
comment
@ Джейсон - прошу прощения. Я был сбит с толку своим мыслительным процессом о том, как я делаю это на С++. - person Jason; 20.07.2011

Вау, это была действительно забавная головоломка. Не часть программирования, а попытка выяснить, откуда вы взяли номер 50516 и что вы пытаетесь сделать со своим кодом. Похоже, вы берете 16 младших значащих битов и ПОВРАЩАЕТЕ их ВЛЕВО на 9 бит.

2402914: 0000 0000 0010 0100 1010 1010 0110 0010
 left 9: 0100 1001 0101 0100 1100 010 
  match:                     ^^^^ ^^^  
>>50516:                     1100 0101 0101 0100
  match:                             ^ ^^^^ ^^^^
right 7:                             1 0101 0100 110 0010

int value2 = value & 0xffff;
int rotate9left = ((value2 << 9) & 0xffff) | ((value2) >> (16 - 9));

Я не знаю, почему вы используете массив байтов, но похоже, что вы думаете, что ваш оператор fixed() зацикливается на массиве, а это не так. Ваш оператор в фиксированном блоке принимает значение байта в myArray[0] и сдвигает его вправо на 15 бит (сдвиг заполняет 0, а не вращение, которое переносит передние биты назад). Любая вещь, превышающая 8, даст вам ноль.

person Jason Goemaat    schedule 20.07.2011

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

Например:

2402914 >> 15 = 73

Это связано с результатом, предсказанным Джейсоном.

Далее отмечу, что

2402914 >> 5 = 75091
and 2402914 >> 6 = 37545

Это говорит о том, что требуемый вам результат не может быть достигнут никаким подобным правым сдвигом.

person Jon Egerton    schedule 20.07.2011