Я всегда предполагал, что при выполнении (a % 256)
оптимизатора, естественно, будет использоваться эффективная побитовая операция, как если бы я писал (a & 0xFF)
.
При тестировании на проводнике компилятора 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
Кажется, я что-то упустил. Любые идеи?
c++
optimization
Элад Вайс
источник
источник
%
не&
либо.num
естьunsigned
?Ответы:
Это не одно и то же. Попробуйте
num = -79
, и вы получите разные результаты от обеих операций.(-79) % 256 = -79
, а(-79) & 0xff
это какое-то положительное число.Используя
unsigned int
, операции одинаковы, и код, скорее всего, будет таким же.PS- Кто-то прокомментировал
Это не так, как это определено в 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 гласит:
Включение оптимизации
Однако, используя
unsigned
типы, оптимизация будет полностью правильной , удовлетворяя вышеуказанному соглашению:Смотрите также это .
Другие языки
Это обрабатывается очень по-разному в разных языках программирования, как вы можете посмотреть в Википедии .
источник
Начиная с C ++ 11,
num % 256
должен быть неположительным, еслиnum
отрицательным.Таким образом, битовая комбинация будет зависеть от реализации подписанных типов в вашей системе: для отрицательного первого аргумента результатом является не извлечение младших значащих 8 битов.
Было бы другое дело, если бы
num
в вашем случае это былоunsigned
: в эти дни я почти ожидал, что компилятор сделает оптимизацию, которую вы цитируете.источник
num
отрицательно, тоnum % 256
равно нулю или отрицательно (иначе не положительно).(-250+256)%256==6
, но(-250%256)+(256%256)
должно быть, согласно стандарту, «неположительным», и, следовательно, нет6
. Такое нарушение ассоциативности имеет реальные побочные эффекты: например, при вычислении рендеринга "уменьшения масштаба" в целочисленных координатах необходимо сместить изображение, чтобы все координаты были неотрицательными.(128+128)%256==0
но(128%256)+(128%256)==256
. Возможно, есть хорошее возражение против указанного поведения, но мне не ясно, что это то, что вы сказали.256==0
. Ключ должен иметь точноN
возможные значения по модулюN
арифметики, что возможно, только если все результаты находятся в диапазоне0,...,(N-1)
, а не-(N-1),...,(N-1)
.У меня нет телепатического понимания рассуждений компилятора, но в случае необходимости
%
приходится иметь дело с отрицательными значениями (и делением округляется до нуля), в то время&
как результатом всегда являются младшие 8 бит.Эти
sar
звуки команд мне , как «сдвиг арифметического права», заполняя освободившиеся биты со знаком битового значения.источник
Говоря математически, по модулю определяется следующее:
% b = a - b * этаж (a / b)
Это прямо здесь должно прояснить это для вас. Мы можем исключить floor для целых чисел, потому что целочисленное деление эквивалентно floor (a / b). Однако, если бы компилятор использовал общий трюк, как вы предлагаете, он должен был бы работать для всех a и всех b. К сожалению, это просто не тот случай. Говоря математически, ваш трюк на 100% правильный для целых чисел без знака (я вижу, что состояния ответа знаковые целые числа ломаются, но я могу подтвердить или опровергнуть это, так как -a% b должен быть положительным). Тем не менее, вы можете сделать этот трюк для всех б? Возможно нет. Вот почему компилятор этого не делает. В конце концов, если бы модуль был легко записан как одна побитовая операция, то мы просто добавили бы схему по модулю, как для сложения и других операций.
источник
-2.3
является-3
, в то время как если вы усечь-2.3
до целого числа вы получите-2
. См. En.wikipedia.org/wiki/Truncation . msgstr "усечение отрицательных чисел не округляется в том же направлении, что и функция пола". И поведение%
отрицательных чисел как раз и является причиной того, что ОП видит описанное поведение.