Источник мой ответ на:Целочисленное деление на 7
Is this expression correct in C preprocessor
Я немного из моего форте здесь, и я пытаюсь понять, как это конкретные оптимизации работы.
Как уже говорилось в ответе, GCC оптимизирует целочисленное деление на 7:
mov edx, -1840700269
mov eax, edi
imul edx
lea eax, [rdx+rdi]
sar eax, 2
sar edi, 31
sub eax, edi
Что переводится обратно в C, как:
int32_t divideBySeven(int32_t num) {
int32_t temp = ((int64_t)num * -015555555555) >> 32;
temp = (temp + num) >> 2;
return (temp - (num >> 31));
}
Давайте посмотрим на первую часть:
int32_t temp = ((int64_t)num * -015555555555) >> 32;
Почему этот номер?
Ну, давайте возьмем 2^64 и разделим его на 7 и посмотрим, что всплывает.
2^64/7 = 2635249153387078802.28571428571428571429
Это похоже на беспорядок, что, если мы преобразуем его в восьмеричное?
0222222222222222222222.22222222222222222222222
Это очень симпатичный повторяющийся шаблон, безусловно, не может быть совпадением. Я имею в виду, что 7 - это 0b111
, и мы знаем, что когда мы делим на 99, мы склонны получать повторяющиеся шаблоны в базе 10. Таким образом, имеет смысл, что мы получим повторяющийся шаблон в базе 8, когда разделим на 7.
Итак, где наш номер входит?
(int32_t)-1840700269
такая же, как (uint_32t)2454267027
* 7 = 17179869189
И, наконец, 17179869184 является 2^34
Это означает, что 17179869189 является самым близким кратным 7 2^34. Или, это еще один способ 2454267027 наибольшее число, которое помещается в uint32_t
, которое при умножении на 7 очень близка к мощности 2
Что это число в восьмеричной системе?
0222222222223
Почему это важно? Ну, мы хотим разделить на 7. Это число составляет 2^34/7 ... приблизительно. Поэтому, если мы умножим его, а затем сдвинем влево 34 раз, мы должны получить число, очень близкое к точному числу.
Последние две строки выглядят так, как будто они предназначены для исправления ошибок аппроксимации.
Возможно, кто-то, у кого есть немного больше знаний и/или опыта в этой области, может прослушивать это.
>>> magic = 2454267027
>>> def div7(a):
... if (int(magic * a >> 34) != a // 7):
... return 0
... return 1
...
>>> for a in xrange(2**31, 2**32):
... if (not div7(a)):
... print "%s fails" % a
...
Неудачи начинаются в 3435973841, который, как ни странно 0b11001100110011001100110011010001
Классифицируя почему приближение не удается немного за меня, и почему патчи исправить это как хорошо.Кто-нибудь знает, как волшебство работает дальше того, что я здесь положил?
http://www.hackersdelight.org/divcMore.pdf –
Этот документ был очень полезен при определении того, для чего была последняя строка (sign fix up); однако, похоже, он не обсуждал этот алгоритм, в частности, если я не пропустил его. – OmnipotentEntity
Окончательные ссылки [здесь] (http://gmplib.org/~tege/divcnst-pldi94.pdf) (реализовано в компиляторе gcc) и последующие действия [здесь] (http://gmplib.org/~tege/division-paper.pdf). Реализации можно найти в библиотеке [GMP] (http://gmplib.org/). ('udiv_qrnnd_preinv' в' gmp-impl.h') –