Źródło moja odpowiedź w:Integer dzielenie przez 7
Is this expression correct in C preprocessor
jestem trochę z mojego forte tutaj, a ja staram się zrozumieć, jak to szczególne prace optymalizacyjne.
Jak wspomniano w odpowiedzi, gcc będzie optymalizować podział całkowitą od 7 do:
mov edx, -1840700269
mov eax, edi
imul edx
lea eax, [rdx+rdi]
sar eax, 2
sar edi, 31
sub eax, edi
co przekłada się z powrotem do C jak:
int32_t divideBySeven(int32_t num) {
int32_t temp = ((int64_t)num * -015555555555) >> 32;
temp = (temp + num) >> 2;
return (temp - (num >> 31));
}
Rzućmy okiem na pierwszej części:
int32_t temp = ((int64_t)num * -015555555555) >> 32;
Dlaczego ten numer?
Weźmy 2^64 i podzielmy przez 7 i zobaczmy, co wyskoczy.
2^64/7 = 2635249153387078802.28571428571428571429
To wygląda jak bałagan, a jeśli skonwertujemy go na ósemkowy?
0222222222222222222222.22222222222222222222222
To bardzo ładny wzór powtarzalny, na pewno nie może to być zbieg okoliczności. Mam na myśli to, że pamiętamy, że 7 to 0b111
i wiemy, że kiedy dzielimy przez 99, mamy tendencję do powtarzania wzorców w bazie 10. To ma sens, że otrzymamy powtarzający się wzór w bazie 8, gdy podzielimy się przez 7.
Więc gdzie jest nasz numer?
(int32_t)-1840700269
jest taka sama jak (uint_32t)2454267027
* 7 = 17179869189
Wreszcie 17179869184 jest 2^34
Czyli 17179869189 jest najbliżej wielokrotnością 7 2^34. Lub umieścić to w inny sposób 2454267027 jest największą liczbą, która zmieści się w uint32_t
która po pomnożeniu przez 7 jest bardzo blisko do potęgi 2
Co to jest liczba w systemie ósemkowym?
0222222222223
Dlaczego jest to ważne? Cóż, chcemy podzielić przez 7. Ta liczba to 2^34/7 ... w przybliżeniu. Jeśli pomnożymy przez to, a następnie przejdziemy 34 razy, powinniśmy uzyskać liczbę bardzo bliską dokładnej liczbie.
Dwie ostatnie linie wyglądają tak, jakby zostały zaprojektowane w celu łatania błędów związanych z aproksymacją.
Być może ktoś, kto ma nieco więcej wiedzy i/lub doświadczenia w tej dziedzinie, może w tym pomóc.
>>> 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
...
Awarie rozpocząć na 3435973841, która jest na tyle zabawnie 0b11001100110011001100110011010001
Klasyfikowanie dlaczego zbliżanie nie jest trochę poza mną i dlaczego łatki naprawić go jest tak dobrze.Czy ktoś wie, jak działa magia ponad to, co tu umieściłem?
http://www.hackersdelight.org/divcMore.pdf –
Ten plik PDF był bardzo pomocny w określeniu, do czego służyła ostatnia linia (poprawka znaku); jednak wydaje się, że nie omawiałem tego algorytmu w szczególności, chyba że go przegapiłem. – OmnipotentEntity
Ostateczne odniesienia to [tutaj] (http://gmplib.org/~tege/divcnst-pldi94.pdf) (zaimplementowany w kompilatorze gcc) i kontynuacja [tutaj] (http://gmplib.org/~tege/division-paper.pdf). Implementacje można znaleźć w bibliotece [GMP] (http://gmplib.org/). ('udiv_qrnnd_preinv' w' gmp-impl.h') –