Очень интересный вопрос и хитрый трюк.
Давайте посмотрим на простой пример манипулирования одним байтом. Использование 8-битного без знака для простоты. Представь, что твой номер есть xxaxxbxx
и ты хочешь ab000000
.
Решение состояло из двух этапов: немного маскировки с последующим умножением. Битовая маска - это простая операция И, которая превращает неинтересные биты в нули. В приведенном выше случае, ваша маска будет 00100100
и результат 00a00b00
.
Теперь самое сложное: превратить это в ab......
.
Умножение - это набор операций сдвига и сложения. Ключ должен позволить переполнению «сдвинуть» ненужные нам биты и поместить нужные нам в нужное место.
Умножение на 4 ( 00000100
) сместит все, что осталось, на 2 и приведет вас к a00b0000
. Чтобы заставить b
двигаться вверх, нам нужно умножить на 1 (чтобы держать a в нужном месте) + 4 (чтобы переместить b вверх). Эта сумма равна 5, и в сочетании с предыдущими 4 мы получаем магическое число 20 или 00010100
. Оригинал был 00a00b00
после маскировки; умножение дает:
000000a00b000000
00000000a00b0000 +
----------------
000000a0ab0b0000
xxxxxxxxab......
При таком подходе вы можете расширить до больших чисел и больше битов.
Один из заданных вами вопросов был: «Можно ли это сделать с любым количеством бит?» Я думаю, что ответ «нет», если вы не разрешаете несколько операций маскирования или несколько умножений. Проблема в проблеме «столкновений» - например, «заблудившийся б» в задаче выше. Представьте, что нам нужно сделать это с таким числом, как xaxxbxxcx
. Следуя более раннему подходу, вы можете подумать, что нам нужно {x 2, x {1 + 4 + 16}} = x 42 (оооо - ответ на все вопросы!). Результат:
00000000a00b00c00
000000a00b00c0000
0000a00b00c000000
-----------------
0000a0ababcbc0c00
xxxxxxxxabc......
Как видите, он все еще работает, но «только что». Ключевым моментом здесь является то, что между битами, которые мы хотим, есть «достаточно места», чтобы мы могли все сжать. Я не мог добавить четвертый бит d сразу после c, потому что я получал случаи, когда я получал c + d, биты могли нести, ...
Поэтому без формального доказательства я бы ответил на более интересные части вашего вопроса следующим образом: «Нет, это не будет работать для любого количества бит. Чтобы извлечь N бит, вам нужно (N-1) пробелы между битами, которые вы хотите извлекать или иметь дополнительные шаги умножения маски. "
Единственное исключение, которое я могу придумать для правила «должны иметь (N-1) нули между битами», заключается в следующем: если вы хотите извлечь два бита, смежных друг с другом в оригинале, и хотите сохранить их в тот же порядок, тогда вы все еще можете сделать это. А для правила (N-1) они считаются двумя битами.
Есть еще одна идея, вдохновленная ответом @Ternary ниже (см. Мой комментарий там). Для каждого интересного бита вам нужно только столько нулей справа от него, сколько вам нужно места для битов, которые должны идти туда. Но также нужно столько же битов слева, сколько и битов результата слева. Таким образом, если бит b оказывается в позиции m из n, то он должен иметь нули m-1 слева и нули nm справа. Особенно, когда биты не находятся в том же порядке в исходном номере, как они будут после переупорядочения, это является важным улучшением к первоначальным критериям. Это означает, например, что 16-битное слово
a...e.b...d..c..
Может быть сдвинут в
abcde...........
хотя между e и b есть только один пробел, два между d и c, три между остальными. Что случилось с N-1? В этом случае a...e
становится «один блок» - они умножаются на 1, чтобы оказаться в нужном месте, и поэтому «мы получили е бесплатно». То же самое верно для b и d (b нужно три пробела справа, d нужно те же три слева). Поэтому, когда мы вычисляем магическое число, мы обнаруживаем, что есть дубликаты:
a: << 0 ( x 1 )
b: << 5 ( x 32 )
c: << 11 ( x 2048 )
d: << 5 ( x 32 ) !! duplicate
e: << 0 ( x 1 ) !! duplicate
Понятно, что если вы хотите, чтобы эти числа были в другом порядке, вам пришлось бы расположить их дальше. Мы можем переформулировать (N-1)
правило: «Оно всегда будет работать, если между битами будет не менее (N-1) пробелов, или, если известен порядок битов в конечном результате, то если бит b окажется в позиции m n, он должен иметь нули m-1 слева и нули nm справа ".
@Ternary указал, что это правило не совсем работает, так как может быть переход от битов, добавляющих «только справа от целевой области», а именно, когда все искомые биты являются единицами. Продолжая пример, который я привел выше, с пятью плотно упакованными битами в 16-битном слове: если мы начнем с
a...e.b...d..c..
Для простоты я назову битовые позиции ABCDEFGHIJKLMNOP
Математика, которую мы собирались сделать, была
ABCDEFGHIJKLMNOP
a000e0b000d00c00
0b000d00c0000000
000d00c000000000
00c0000000000000 +
----------------
abcded(b+c)0c0d00c00
До сих пор мы думали, что что-либо ниже abcde
(позиции ABCDE
) не будет иметь значения, но на самом деле, как указывало @Ternary, если b=1, c=1, d=1
тогда (b+c)
в позиции G
будет немного переходить в позицию F
, что означает, что (d+1)
в позиции F
будет немного переходить E
- и наша результат испорчен. Обратите внимание, что пробел справа от наименее значимого бита, представляющего интерес ( c
в этом примере), не имеет значения, так как умножение приведет к заполнению нулями от любого младшего значащего бита.
Поэтому нам нужно изменить наше правило (m-1) / (nm). Если имеется более одного бита, который имеет «точно (нм) неиспользуемые биты справа (не считая последний бит в шаблоне -« с »в примере выше), то нам нужно усилить правило - и мы должны делать это итеративно!
Мы должны смотреть не только на число битов, которые соответствуют критерию (нм), но также и на те, которые имеют (n-m + 1) и т. Д. Давайте назовем их число Q0 (точно n-m
следующим битом), Q1 ( n-m + 1), до Q (N-1) (n-1). Тогда мы рискуем нести, если
Q0 > 1
Q0 == 1 && Q1 >= 2
Q0 == 0 && Q1 >= 4
Q0 == 1 && Q1 > 1 && Q2 >=2
...
Если вы посмотрите на это, вы увидите, что если вы напишите простое математическое выражение
W = N * Q0 + (N - 1) * Q1 + ... + Q(N-1)
и в результате W > 2 * N
вы должны увеличить критерий RHS на один бит до (n-m+1)
. На данный момент, операция безопасна, пока W < 4
; если это не сработает, увеличьте критерий еще раз и т. д.
Я думаю, что следуя вышесказанному, вы получите длинный путь к вашему ответу ...