Могу ли я использовать умножение и деление для сдвига битов в программировании на С? - PullRequest
2 голосов
/ 04 февраля 2009

Вместо использования >> и << для сдвига, возможно ли использовать * и / для сдвига влево и вправо?

Для 8-бит: 0x01 * 2 = 0000 | 0010.

Ответы [ 9 ]

17 голосов
/ 04 февраля 2009

Конечно, вы можете (для целочисленной математики) использовать умножение на два для сдвига влево и деление на два для сдвига вправо.

Тебе не следует этого делать.

Есть совершенно хорошие операторы левого и правого сдвига, и они делают "именно то, что говорят на жестяной банке". Зачем путать ваш компилятор или кого-либо еще, кто читает ваш код, поступая иначе?

9 голосов
/ 04 февраля 2009

Можно, но с чего бы вы этого хотели? Сдвиговые операции - это то, что ЦП может делать быстрее всего.

На самом деле, в качестве оптимизации производительности часто предлагается противоположное: использование сдвигов для реализации умножения / деления на степень 2.

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

8 голосов
/ 04 февраля 2009

Вам следует избегать сдвига во всех случаях, кроме случаев, когда вы выполняете битовые операции. Запись << вместо * 2 делает код менее разборчивым. Не используйте сдвиг битов, если вы явно не хотите. </p>

Компиляторы оптимизируют *2 n до << в любом случае.

5 голосов
/ 04 февраля 2009

Вы могли бы, но это был бы умный компилятор, чтобы оптимизировать это в реальных сдвигах битов! Кроме того, если вы имеете в виду «сдвиг», вам лучше написать <<. Когда вы имеете в виду «умножить», напишите *. </p>

Обратите внимание, что плавающие точки не будут смещаться вообще: их показатель будет только расти.

3 голосов
/ 04 февраля 2009

Да, за исключением одного гоча: деление может работать иначе, чем смещение вправо при работе с отрицательными числами. Поведение правых сдвигов на отрицательных числах определяется компилятором.

Конечно, вы можете избежать этой проблемы, используя целые числа без знака.

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

1 голос
/ 04 февраля 2009

Можно,

x << 1 == x * 2
x << 2 == x * 4
etc...

и разговорчиво

x >> 1 == x / 2
x >> 2 == x / 4
etc...

Хотя я бы сказал, что опытный c-программист должен иметь возможность взглянуть на операцию сдвига битов и знать, что оба метода аналогичны. Если бы у меня был выбор, я бы всегда использовал сдвиг битов, поскольку ясно, каковы ваши намерения. Если бы вы использовали опцию умножения / или деления, вам явно нужно было бы прокомментировать, почему вы вдруг умножаете значения на кажущиеся случайными числа. Принимая во внимание, что сдвиг битов четко указывает на то, что вы пытаетесь сдвинуть биты.

Как и для любых комментариев по оптимизации, если абсолютно не требуется, например. критичное по времени программирование, я бы не стал чрезмерно беспокоиться об оптимизации вашего кода. Читаемость и удобство сопровождения кода должны быть в центре вашего внимания, а не в оптимизации! Если в конце разработки вам понадобится больше производительности, вы всегда можете вернуться и выполнить оптимизацию (убедившись, что вы тщательно документируете все, что делаете!). Но во время разработки я бы пошел на то, что легче всего читать и поддерживать, например. используя '<<' / '>>'.

1 голос
/ 04 февраля 2009

Да, вы можете. Принимая ваш пример:

int main() {
  if ((1*2) == (1 << 1)) printf("you certainly can!\n");
  else printf("doesn't work that way\n");
}

И вывод, который он производит:

you certainly can!

См. Комментарии к этому ответу относительно того, почему мы согласились с тем, что (0x01 * 2 == 0000 | 0010) намного более проблематично, чем ((1 * 2) == (1 << 1)), гораздо более кратко версия. </p>

0 голосов
/ 05 февраля 2009

Да, вы всегда можете заменить смены степенью 2 умножения, но, как уже было указано, почему? Что не было отмечено, так это то, что иногда полезно заменить умножение на сдвиг, а не только для простых степеней 2. Например, в старые добрые времена с 320 * 200 пиксельными буферами смещение в пиксельный байт было y * 320 + х. Это можно сделать следующим образом:

int OffsetXY( int x, int y)
{
    int offset;

    y <<= 6;
    offset = y;
    offset += y << 2;  // offset = original y * 320
    return offset + x;
}

Несмотря на то, что этот вид в настоящее время представляет лишь академический интерес для большинства разработчиков, он может быть полезен во встроенном мире (недавно было написано некоторое кодирование на DS, и подобные вещи были полезны).

0 голосов
/ 04 февраля 2009

Все остальные сказали, что это вполне возможно сделать. Это также бессмысленно. Если вы хотите сдвинуться, то сдвиньтесь. Если вы хотите умножить, то умножьте. Не путайте читателя своего кода с бесполезной «оптимизацией».

Помимо вопроса:

Ваш компилятор будет смотреть на любое целочисленное умножение / деление на постоянную величину и почти все время сокращать выражение до серии операций сдвига / сложения. Это происходит потому, что двоичное умножение - непростая задача, и оно занимает намного больше времени, чем сдвиги и сложения, которые могут быть выполнены за один такт после их передачи в ALU. Интересно, что целочисленный множитель в ЦП работает примерно одинаково, но в автоматическом режиме - все еще немного быстрее использовать отдельные операции сдвига / добавления, потому что вам не нужно останавливаться и тестировать условия для каждого уровня сдвига, который вы делаете. *

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

Эта оптимизация особенно верна для умножений на константы многообразия 2 ^ n, потому что это операция с одним сдвигом.

...