В восторге от хакера есть алгоритм для вычисления двухсловного произведения двух (подписанных) слов.
Функция muldws1
использует четыре умножения и пять дополнений для вычисления
двойное слово из двух слов.
В конце этого кода есть строка, прокомментированная
/* w[1] = u*v; // Alternative. */
В этой альтернативе используются пять умножений и четыре сложения, т.е. они обмениваются добавлением для умножения.
Но я думаю, что этот альтернативный метод можно улучшить. Я пока ничего не сказал об оборудовании. Возьмем гипотетический процессор, который может вычислить нижнее слово произведения двух слов, но не верхнее слово (например, для 32-разрядных слов 32x32, чтобы опустить 32). В этом случае мне кажется, что этот алгоритм может быть улучшен. Вот что я придумал предполагая 32-битные слова (одна и та же концепция будет работать для 64-битных слов).
void muldws1_improved(int w[], int32_t x, int32_t y) {
uint16_t xl = x; int16_t xh = x >> 16;
uint16_t yl = y; int16_t yh = y >> 16;
uint32 lo = x*y;
int32_t t = xl*yh + xh*yl;
uint16_t tl = t; int16_t th = t >>16;
uint16_t loh = lo >> 16;
int32_t cy = loh<tl; //carry
int32_t hi = xh*yh + th + cy;
w[0] = hi; w[1] = lo;
}
Это использует четыре умножения, три дополнения и одно сравнение. Это меньшее улучшение, чем я надеялся.
Можно ли это улучшить? Есть ли лучший способ определить флаг переноса?. Я должен указать, что я также предполагаю, что на оборудовании нет флага переноса (например, нет инструкции ADDC), но слова можно сравнить (например, word1<word
).
Изменить: как сказал Sander De Dycker, моя функция терпит неудачу в модульных тестах. Вот версия, которая проходит модульные тесты, но менее эффективна. Я думаю, что это можно улучшить.
void muldws1_improved_v2(int w[], int32_t x, int32_t y) {
uint16_t xl = x; int16_t xh = x >> 16;
uint16_t yl = y; int16_t yh = y >> 16;
uint32_t lo = x*y;
int32_t t2 = xl*yh;
int32_t t3 = xh*yl;
int32_t t4 = xh*yh;
uint16_t t2l = t2; int16_t t2h = t2 >>16;
uint16_t t3l = t3; int16_t t3h = t3 >>16;
uint16_t loh = lo >> 16;
uint16_t t = t2l + t3l;
int32_t carry = (t<t2l) + (loh<t);
int32_t hi = t4 + t2h + t3h + carry;
w[0] = hi; w[1] = lo;
}
Это использует четыре умножения, пять добавлений и два сравнения, что хуже, чем исходная функция.