Не могу придумать формулу. Задача несколько специфическая. Предположим, что у нас есть некоторая переменная, которая может хранить число из диапазона от 0 до 9.
Надо придумать как умножить такие переменные.
Диапазон следует представлять как замкнутую ленту из последовательных ячеек 0, 1, 2, ..., 9, таким образом если мы пишем 9+1, то попадаем в ячейку 0 (т.е. 9+1=0), и так далее вплоть до 9+9=8.
Дано:
max = 9 - это предельное значение диапазона,
a - одна переменная,
b - другая переменная
carry - переменная переноса.
Операции:
+, -, *, /
битовые |, &, ^, ! (or, and, xor, not)
% - взятие остатка
ну и любые другие можно использовать
Примеры:
a=1, b=9 => carry = 0
a=2, b=9 => carry = 1
a=4, b=8 => carry = 3
Ограничения:
переменных способных хранить числа, состоящие более, чем из одного знака, не существует. Это означает, что операция 2*9 невыполнима в стандартном понимании, так как не существует переменной способной хранить число 18, результатом операции 2*9 будет 2*9=9+9=8 как уже говорилось. Данное ограничение можно воспринимать как своеобразную архитектуру процессора, который работает только лишь с однозначными числами.
Найти:
функцию carry = f(a,b,max).
Например, при 2*9 нахождение carry=1 позволит отпечатать произведение как последовательная печать переменной carry как 1 и печать операции 2*9 как 8, итого 18.
Надо придумать как умножить такие переменные.
Диапазон следует представлять как замкнутую ленту из последовательных ячеек 0, 1, 2, ..., 9, таким образом если мы пишем 9+1, то попадаем в ячейку 0 (т.е. 9+1=0), и так далее вплоть до 9+9=8.
Дано:
max = 9 - это предельное значение диапазона,
a - одна переменная,
b - другая переменная
carry - переменная переноса.
Операции:
+, -, *, /
битовые |, &, ^, ! (or, and, xor, not)
% - взятие остатка
ну и любые другие можно использовать
Примеры:
a=1, b=9 => carry = 0
a=2, b=9 => carry = 1
a=4, b=8 => carry = 3
Ограничения:
переменных способных хранить числа, состоящие более, чем из одного знака, не существует. Это означает, что операция 2*9 невыполнима в стандартном понимании, так как не существует переменной способной хранить число 18, результатом операции 2*9 будет 2*9=9+9=8 как уже говорилось. Данное ограничение можно воспринимать как своеобразную архитектуру процессора, который работает только лишь с однозначными числами.
Найти:
функцию carry = f(a,b,max).
Например, при 2*9 нахождение carry=1 позволит отпечатать произведение как последовательная печать переменной carry как 1 и печать операции 2*9 как 8, итого 18.
Вывод
mul: fefe 1
Должно быть:
mul: fffe 1
Думаю, что ужать код ещё можно.
p.s.
хотя нет, неправильно при
uint16 b[] = { 0xaaui16, 0xffui16 }; // 43775
считает
Но тебя вроде просили реализовать умножение двух 32-битных чисел, а не 16-битных.
> посмотрел книги, которые вы рекомендовали,
> верилог всё же другое как по мне дк.
Сумматоры - это чисто комбинационная логика и легко переносятся на Си (в отличие от схем с предыдущем состоянии на входе - типа триггеров).
И алгоритмы в книжке самые оптимальные. Поэтому и порекомендовал.
А если её всю прочитать и проделать лабы - это ключ к сакральному знанию о том, как делается сейчас в мире в корпоративных гигантах вся современная продвинутая цифровая электроника на низком уровне.
Работает, это хорошо.
походу не работает из-за неправильной реализации сложения, сейчас проснулся и заметил, что
случай 65535 + 1 вместо
sum: 1 0
у меня покажет
sum: 0 0,
причём я понимаю почему так, но пока не понимаю как это поправить...
Но тебя вроде просили реализовать умножение двух 32-битных чисел, а не 16-битных.
да, всё так, но умножение 32-битных будет базироваться на сложении и умножении 16-битных, поэтому пока операции с ними и обкатываю
Ок. Но я бы сделал:
a) функцию умножения 32-битного числа на A < 256,
b) функцию сдвига 32-битного числа на b бит
с) цикл, в котором реализуется алгоритм умножения столбиком
(сложение у тебя уже есть)
Это вроде как проще запрогать, и меньше шансов запутаться и допустить ошибку.
причём я понимаю почему так, но пока не понимаю как это поправить...
удобнее представлять, что у тебя 256-ричная система счисления.
попробуй на бумаге сложить и не 16-битные числа, а чё-нить подлиннее, выйдет универсальный алгоритм
Хотя мб тебе проще твоим подходом добить до конца
Но есть сомнения в этом куске
На примере в комментариях carry это переполнение при сложении ffee + 5600, в общем случае y1 (ffee) + y2 (ff56), но y2 будет всегда же сдвинут на байт влево по алгоритму умножения столбиком, тогда будет ffee + 5600, в общем случае y1 + (y2 << 8), причём младший байт y1 это ee, в общем случае (y1 & 0x00ff), а младший байт y2 это всегда 00, поэтому их сумма никогда не должна давать переполнения в старший байт, а тогда вроде бы достаточно просто так записать:
то есть не учитывать сумму ( (y1 & 0x00ff) + ((y2 << 8) & 0x00ff) ) >> 8
Вывод:
sum: 2 9998
Должно быть:
sum: 1 9998
То есть к переносу по старшему байту добавляет перенос по младшему байту... и получается неверный перенос
Груша Вильямс, именно поэтому придумали массивы, функции и циклы!
например,
ab
cd
1) b*d и от него carry_bd и res[3]
2a) a*d и от него carry_ad и 1-ый res[2]
2б) b*c и от него carry_bc и 2-ой res[2]
2в) a*d + b*c и от него carry_ad_bc и 3-ий res[2] = 1-ый res[2] + 2-ой res[2]
2г) a*d + b*c + carry_bd и от него carry_ad_bc_carry и 4-ый res[2] = 3-ий res[2] + carry_bd
3) как и два можно сказать...
Непонятно как такую логику зацикливать, в первую очередь непонятно с переменной carry, возможно с одной переменной это провернуть или нет, по-моему с одной нельзя, напрашиваются carry_add на сложение и carry_mul на умножение, та же проблема с res[2] на разных шагах
Перепишу с carry_add и carry_mul:
1) b*d и от него carry_mul и res[3]
2a) a*d и от него новый carry_mul -- выходит, что предыдущее значение carry_mul перетирается
2б) b*c и от него новый carry_mul -- проблема как и на предыдущем шаге
2в) a*d + b*c и от него carry_add -- тут проблем нет
2г) a*d + b*c + (нужен самый первый carry_mul, тот который carry_bd, тогда получается, что carry_mul на шаге 1 и carry_mul на шаге 2 должны быть разные) и от него carry_add суммируется с предыдущим значением carry_add (т.е. carry_add += carry_add)
3) как и два можно сказать...
а дальше собственно ступор...
xx98
xx76
______
xx48
x56
x54
63
_______
из-за того, что 54 не сдвинуто на знак относительно предыдущего частичного произведения 56, получается, что общая логика рушится, то есть для элементов a[1]*b[0] и a[0]*b[1] будет немного другая логика, без смещения на знак, в этом и проблема основная как мне кажется. Но каждое из частичных произведений (48, 56, 54 и 63) является числом типа uint16, которое легко получить через ту же функцию mul_16(). В принципе в цикле легко собрать массив этих частичных произведений, а потом уже как-то с ним там работать. Вроде как это упрощение задачи уже будет, как минимум переход от умножения к сложению произошёл.
Пример
Надо переполнение при подсчёте y1 как-то отловить, оно возникает как раз при сложении...
частичные произведения написал
Вроде работает.
Если нажать fork, можно редактировать и запускать старый код там.
P.S. Через ООП получилось бы компактнее, но ведь нужно было на Си.