2

В статье я прочитал, что деление и умножение числа на степень 2 являются тривиальным процессом. Я искал много интернета для объяснения, но не понял. Может ли кто-нибудь объяснить легкими словами, что это на самом деле означает сказать.деление и умножение по мощности 2

+1

Это то же самое, что разделение и умножение на 10 для людей. Как вы можете умножить любое число на 10? Просто добавьте ноль. – usr2564301

+1

Будьте осторожны: как только вы попытаетесь отслеживать переполнение при умножении или остаток при делении, это может быть не так тривиально, как вы надеялись. –

ответ

3

Это тривиально с точки зрения операций с битами. Умножение на 2 эквивалентно сдвигу влево на 1 бит, деление - сдвиг вправо. так же это то же самое тривиальное умножение и деление на любую степень 2.

int a = 123;   // or in binary format: a = 0b01111011; 

assert (a * 2) == (a << 1); // 2 = 2^1, (a << 1) = 11110110 
assert (a/2) == (a >> 1); // 2 = 2^1, (a >> 1) = 00111101 

assert (a * 8) == (a << 3); // 8 = 2^3, (a << 3) = 1111011000 
assert (a/8) == (a >> 3); // 8 = 2^3, (a >> 3) = 0000001111 

Также отметим, что a*2 = a+a, и добавление иногда даже дешевле, чем меняется, в зависимости от оборудования.

Для подписанного деления обратите внимание на то, что на некоторых языках (например, C) целочисленное деление усекается к нулю, тогда как арифметический сдвиг вправо (сдвиг в копиях знакового бита для дополнения 2) всегда округляется к -Infinity. например (-5)/2 == -2, но (-5) >> 1 == -3. Реализация подписанного деления на 2 может выполняться со сдвигом + дополнительные операции, чтобы добавить бит знака, чтобы получить поведение усечения. (Посмотрите на вывод компилятора C.)

1

В основном умножать и делить числа на степень 2, если число выражается в двоичной системе, нужно просто перевести все двоичный разряд влево или вправо:

00100, что является 4, если вы хотите умножить на 2 * 2 * 2 вы просто перевести все количество осталось 3 раза:

100000, что является exaqctly 32 (4 * 8 = 32)

к разделим это наоборот

2

с все числа хранятся в двоичном умножении ation/division - операция простого битового сдвига.

Например (умножение):

  • 5 = 101 (двоичный)
  • 5 * 2 = 10 = 1010 (двоичный) - просто сдвинуты все биты 1 позицию влево
  • 5 * 4 = 20 = 10100 (двоичный) - только сдвинутые все биты 2 позиции влево

То же самое относится к делению (операция правого битового сдвига), но вам необходимо рассмотреть перенос для деления нечетных чисел, если вам нужен остаток.

2

Важно в этом контексте то, что деление на 2 (или на мощность 2) тривиально только для целочисленной арифметики. Это становится менее тривиальным, когда вы говорите о делении с плавающей запятой.

Причина этого заключается в том, что деление на базовое число некоторой системы цифр (двоичный, восьмеричный, шестнадцатеричный, вы называете его) всегда может быть выполнено простым сдвигом вправо числа.

Например, в десятичной системе с разделением по десять у вас есть:

230.0/10.0 = 23.00 

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

же для шестнадцатеричных чисел:

0xA2FF/0x10 = 0xA2F 

(номер сдвигается на одну позицию вправо)

И то же самое для двоичных чисел:

1101011/10 = 110101 (binary notation) 
107 /2 = 53  (decimal notation for the same equation) 

Если вы хотите разделить на степень двойки, число правых сдвигов, которые необходимо выполнить, соответствует показателю. Например, деление на 4 означает деление на 2 * 2, равное двум операциям с правом сдвига:

1101011/100 = 11010 (binary notation, two shift operations) 
107 / 4 = 26 (decimal notation) 
+0

Бинарные форматы с плавающей запятой (такие как [IEEE754 binary64 aka 'double'] (https://en.wikipedia.org/wiki/Double-precision_floating-point_format)) могут дешево размножаться или делить по степеням 2, просто добавляя или вычитая из поля экспоненты! (Тем не менее, вы должны проверить переполнение). Аппаратное обеспечение не всегда обеспечивает это как отдельную операцию, поэтому часто вы просто умножаете ее на '0,5' с помощью обычной инструкции. AVX512F делает это с помощью '' vscalefsd' (масштаб скалярного двойника на '2^floor (x)')] (https://hjlebbink.github.io/x86doc/html/VSCALEFSD.html) и связанных версий с плавающей точкой и SIMD –

+0

Наверное, это то, что вы подразумевали под * менее * тривиальным, а не * не * тривиальным, но для HW довольно сложно реализовать. –

Смежные вопросы