Почему (a % 256) отличается от (A & 0xFF)?
Я всегда предполагал, что при выполнении (a % 256) оптимизатор, естественно, будет использовать эффективную побитовую операцию, как если бы я написал (a & 0xFF).
при тестировании на компиляторе explorer gcc-6.2 (- O3):
// Type your code here, or load an example.
int mod(int num) {
return num % 256;
}
mod(int):
mov edx, edi
sar edx, 31
shr edx, 24
lea eax, [rdi+rdx]
movzx eax, al
sub eax, edx
ret
и при попытке другого кода:
// Type your code here, or load an example.
int mod(int num) {
return num & 0xFF;
}
mod(int):
movzx eax, dil
ret
кажется, что я полностью упускаю что-то.
Есть идеи?
5 ответов:
это не то же самое. Попробуй
num = -79, и вы получите разные результаты от обеих операций.(-79) % 256 = -79, в то время как(-79) & 0xffнекоторое положительное число.используя
unsigned int, операции одинаковы, и код, вероятно, будет таким же.PS - кто-то прокомментировал
они не должны быть одинаковыми,
a % bопределяется какa - b * floor (a / b).это не так, как это определено в C, C++, Objective-C (т. е. все языки, где код в вопросе будет компилироваться).
короткий ответ:
-1 % 256доходность-1, а не255что это-1 & 0xFF. Поэтому оптимизация будет некорректной.ответ
C++ имеет соглашение, что
(a/b)*b + a%b == a, которое кажется вполне естественным.a/bвсегда возвращает арифметический результат без дробной части (усечение до 0). Как следствие,a%bимеет тот же знак,aили равно 0.отдела
-1/256доходность0и отсюда-1%256должно быть-1для выполнения вышеуказанного условия ((-1%256)*256 + -1%256 == -1). Это явно отличается от-1&0xFFчто это0xFF. Следовательно, компилятор не может оптимизировать так, как вы хотите.соответствующий раздел стандарт C++ [expr.mul §4] по состоянию на N4606 гласит:
для целочисленных операндов
/оператор дает алгебраическое частное с любой отброшенной дробной частью; если частноеa/bis представимый в виде результата,(a/b)*b + a%bравнаa[...].включение оптимизации
, используяunsignedтипы, оптимизация будет полностью правильной, удовлетворяя вышеуказанной Конвенции:unsigned(-1)%256 == 0xFFсм. также этой.
другие языки
это обрабатывается очень по-разному на разных языках программирования, как вы можете посмотреть на Википедия.
Начиная С C++11,
num % 256должен быть неположительным, еслиnumотрицательный.таким образом, битовый шаблон будет зависеть от реализации подписанных типов в вашей системе: для отрицательного первого аргумента результат не является извлечением наименее значимых 8 бит.
другое дело, если
numв вашем случае былunsigned: в эти дни я почти ожидал компилятор, чтобы сделать оптимизацию, которую вы цитируете.
у меня нет телепатического понимания рассуждений компилятора, но в случае
%существует необходимость иметь дело с отрицательными значениями (и деления раундов к нулю), в то время как с&результат всегда меньше 8 бит.The
sarинструкция звучит для меня как "сдвиг арифметики вправо", заполняя освобожденные биты значением бита знака.
математически говоря, модуль определяется следующим образом:
a % b = a-b * floor (a/b)
Это прямо здесь должно прояснить это для вас. Мы можем исключить пол для целых чисел, потому что целочисленное деление эквивалентно полу(a/b). Однако, если бы компилятор использовал общий трюк, как вы предлагаете, он должен был бы работать для всех a и всех b. к сожалению, это просто не так. Математически говоря, ваш трюк на 100% верны для целых чисел без знака (Я вижу, что ответ состоял из целых чисел со знаком, но я не могу подтвердить или опровергнуть это, поскольку-A % b должен быть положительным). Однако, можете ли вы сделать этот трюк для всех b? Скорее всего, нет. Вот почему компилятор этого не делает. В конце концов, если бы модуль был легко записан как одна побитовая операция, то мы бы просто добавили схему по модулю, как для сложения и других операций.
Comments