Szybki podział na GCC / ARM
O ile wiem, większość kompilatorów wykona szybki podział, mnożąc, a następnie przesuwając bit w prawo. Na przykład, jeśli sprawdziszten wątek SO mówi, że kiedy poprosisz kompilator Microsoft, aby dokonał podziału przez 10, pomnoży dywidendę przez 0x1999999A (czyli 2 ^ 32/10), a następnie podziel wynik przez 2 ^ 32 (używając 32 przesunięć w prawo).
Jak na razie dobrze.
Kiedy jednak przetestowałem ten sam dział przez 10 na ARM przy użyciu GCC, kompilator zrobił coś nieco innego. Najpierw pomnożył dywidendę przez 0x66666667 (2 ^ 34/10), a następnie podzielił wynik przez 2 ^ 34. Do tej pory jest taki sam jak Microsoft, z wyjątkiem używania wyższego mnożnika. Potem jednak odjął (dywidenda / 2 ^ 31) od wyniku.
Moje pytanie: dlaczego w wersji ARM istnieje dodatkowe odejmowanie? Czy możesz podać mi przykład liczbowy, w którym bez tego odejmowania wynik będzie błędny?
Jeśli chcesz sprawdzić wygenerowany kod, poniżej (wraz z moimi komentarzami):
ldr r2, [r7, #4] @--this loads the dividend from memory into r2
movw r3, #:lower16:1717986919 @--moves the lower 16 bits of the constant
movt r3, #:upper16:1717986919 @--moves the upper 16 bits of the constant
smull r1, r3, r3, r2 @--multiply long, put lower 32 bits in r1, higher 32 in r3
asr r1, r3, #2 @--r3>>2, then store in r1 (effectively >>34, since r3 was higher 32 bits of multiplication)
asr r3, r2, #31 @--dividend>>31, then store in r3
rsb r3, r3, r1 @--r1 - r3, store in r3
str r3, [r7, #0] @--this stores the result in memory (from r3)