O ile mi wiadomo, większość kompilatorów zrobi szybki podział przez pomnożenie, a następnie przesunięcie bitów w prawo. Na przykład, jeśli sprawdzisz this SO thread, to powiesz, że gdy poprosisz kompilator Microsoftu o dzielenie przez 10, to pomnoży on dywidendę przez 0x1999999A (która jest 2^32/10), a następnie podzieli wynik przez 2^32 (używając 32 zmian w prawo).Szybki podział na GCC/ARM
Jak dotąd tak dobrze.
Raz przetestowałem ten sam podział przez 10 na ARM przy użyciu GCC, chociaż kompilator zrobił coś nieco innego. Najpierw pomnożył on dywidendę przez 0x66666667 (2^34/10), a następnie podzielił wynik na 2^34. Do tej pory jest taki sam jak Microsoft, z wyjątkiem użycia wyższego mnożnika. Następnie od wyniku odjęto (dywidenda/2^31).
Moje pytanie: dlaczego w wersji ARM jest to dodatkowe odejmowanie? Czy możesz podać przykład liczbowy, gdzie bez tego odejmowania wynik będzie błędny?
Jeśli chcesz sprawdzić wygenerowany kod, to poniżej (z moich komentarzach):
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)
Dla ujemnych wartości, obcięcie dzielenia całkowitoliczbowego, a po prostu mnożenie i przesunięcie powoduje powstanie 'x/10 - 1' dla ujemnego' x'. (Zakładając, że arytmetyczna prawostronna zmiana, oczywiście.) –
Widzę, że jeśli zrobię -99/10 z metodą mnożenia/przesunięcia, otrzymam -10 jako wynik. Ale jeśli odejmiemy 1 od tego, otrzymam -11, kiedy to, czego chcę, to -9, prawda? –
Odejmujesz '-1', tzn. Dodajesz 1. –