Насколько я знаю, большинство компиляторов будет выполнять быстрое деление путем умножения и последующего смещения вправо. Например, если вы отметите this SO thread, в нем говорится, что когда вы попросите компилятор Microsoft выполнить деление на 10, он умножит дивиденд на 0x1999999A (что составляет 2^32/10), а затем разделит результат на 2^32 (используя 32 смены направо).Fast Division on GCC/ARM
Пока все хорошо.
Как только я протестировал одно и то же деление на 10 на ARM, используя GCC, компилятор сделал что-то немного другое. Сначала он умножил дивиденд на 0x66666667 (2^34/10), затем разделил результат на 2^34. До сих пор это то же самое, что и Microsoft, за исключением использования более высокого множителя. После этого, однако, он вычитал (дивиденд/2^31) из результата.
Мой вопрос: почему в версии ARM есть дополнительное вычитание? Можете ли вы дать мне числовой пример, где без этого вычитания результат будет неправильным?
Если вы хотите, чтобы проверить сгенерированный код, это ниже (с моими комментариями):
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)
Это отрицательные значения, целые деления, а просто умножение и сдвиг производят 'x/10 - 1' для отрицательного' x'. (Предположим, конечно, арифметический сдвиг вправо). –
Я вижу, что если я сделаю -99/10 с помощью метода умножения/сдвига, в результате я получу -10. Но если я вычитаю 1 из этого, я получу -11, когда то, что я хочу, это -9, не так ли? –
Вы вычитаете '-1', т. Е. Добавляете 1. –