Битовая манипуляция - понимание округления до нулевого смещения при умножении отрицательного на дробную

У меня есть метод манипуляции с битами, который умножает число на пять восьмых и, если есть остаток, округляется до 0. Метод работает, и я понимаю почти все. Однако, просматривая его, я понял, что не уверен, как anding 7 (00000111) объясняет ошибку округления, при которой оно округляется в сторону более отрицательного числа вместо 0, и мне нужно понять, почему эта строка работает. Насколько я могу видеть, сдвигание multiplyByFive на 31 бит вправо просто проверит знак переменной и выдаст все единицы, если они отрицательные, так что если задать значение 7, то все нули получатся положительными, или y в двоичном, если отрицательными. Если мое понимание правильное, зачем добавлять это к множителю пятьдесят восемь и делить сумму на 8 вокруг отрицательного числа без ошибок.

int multFiveEights(int x) {

разбить его на умножение на 5, а затем разделить на 8, сдвинув его влево на два, умножить на четыре, плюс x сделает его на 5

int multiplyByFive = (x << 2) + x;

если результат отрицательный, а 2^3 - 1 = 7 до сдвига вправо

int addNumber = 7 & (multiplyByFive >> 31); 

11111111 (если вы сдвинете вправо на 31, когда отрицательно, вы получите все 1)

вернет все 0, если положительный, и 1 в младшем бите, если отрицательный

добавление 7 к учетным записям multiplyByFive из-за ошибки

если его значение отрицательное, оно будет пытаться округлить число, которое идет к более отрицательному числу, и, таким образом, добавление 7 к этой ошибке / тесты для остатка

int fiveEigths = (multiplyByFive + addNumber) >> 3;
return fiveEigths;

2 ответа

Решение

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

Итак, логика состоит в том, чтобы добавить 7 к multiplyByFive до смещения вправо на 3, но только когда оно отрицательное.

Чтобы понять, почему это работает, рассмотрим разницу между округлением в меньшую и меньшую сторону в терминах младших 3 битов.

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

Если младшие 3 бита являются чем-то еще, то конечный результат округления будет на один меньше, чем если бы вы округлили в большую сторону.

Добавив 7, вы увеличите конечный результат на 1, синхронизируя 4-й бит в каждом случае, кроме случаев, когда младшие 3 бита равны нулю. Если все они равны 0, добавление 7 установит младшие 3 бита в 1, но не повлияет на 4-й, оставив смещенный результат без изменений.

(multiplyByFive + 0) >> 3 делит на 8 (всегда округляя) и
(multiplyByFive + 7) >> 3 делится на 8 (всегда округляется).

Ваш код всегда округляется до нуля, проверяя, в каком направлении находится ноль, а затем округляя в этом направлении. Если число, которое нужно разделить, является отрицательным, то оно добавляет 7, поэтому оно округляется (что к нулю). Если оно положительное, то оно добавляет 0, поэтому округляется (что также к нулю).

Другие вопросы по тегам