Не могу придумать формулу. Задача несколько специфическая. Предположим, что у нас есть некоторая переменная, которая может хранить число из диапазона от 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.
Поэтому нужно ещё как минимум оговорить, как хранятся числа и (обязательно!) что нужно считать элементарными операциями в этой архитектуре. Иначе в самом простом случае мы можем заявить, что всё реализовано через большую таблицу истинности и задача решена.
Вопрос в определении carry.
Ну. во-первых, если вы обратитесь к рекомендованной литературе, то окажется, что carry - это обычно расширение регистра, и если переполнение случилось, то в дополнительных битах не нуль (то есть carry не вычисляют отдельной формулой), это как бы 32(64)-тый бит регистра (для вашей задачи надо взять не один дополнительный бит, а 4).
Во-вторых, битовые операции несколько странно применять для регистров емкостью не кратных степени 2. Что, например, будет означать битовая операция 5 OR 8?
Про формулу сейчас ещё подумаю.
Код написал в итоге так пока
Работает. Как понимаю вместо девятки UINT_MAX бахнуть и всё будет работать для стандартного 32-разрядного unsigned int.
По задаче же придётся свою константу для наибольшего значения 16-разрядного числа определить насколько понимаю, UINT16_MAX какой-нибудь.
Выводит
Осталось алгоритм умножения подобрать какой-нибудь... И собрать это всё дело.
Классический вариант решения подобной задачи - хранить такое число в массиве. Не нужно никаких carry. Или нельзя uint16[2]?
Что это? Есть архитектурно независимый тип данных uint16_t. Используй его.
В книжках, которые я порекомендовал, есть отдельная глава по сложению n-разрядных чисел и отдельная глава по умножению n-разрядных чисел. Вкратце, делают подробную реалиазацию на небольшом количестве бит, а затем строят простую цепочку этих блоков, т.е. наращивают сумматор до необходимой разрядности.
Если не хочешь глубоко вникать в эту тему, вот тебе лайфхак - реализуй на базе uint16_t арифметику восьмибитных чисел, тогда тебе не придётся вводить отдельную переменную под перенос или умножение, а 32-битное число, это массив из 4 этих чисел, где значимыми являются только последние 8 бит.
сложение, это b[0]=a[0]+a[0]=0x1fe, b[1]=a[1]+a[1]=0x1fe, и переносов: b[1] = (a[0] >> 8) + b[0] и сохранить b[2] перенос из b[1].
Я не очень понял, почему нельзя... Ну если вдруг действительно нельзя, сделай 4-ёх битные на базе 8-ми битных
Ура.
Всё правильно, только результат умножения всё равно из 4ех элементов, старшие биты теряются, хотя в некоторых архитектурах предусмотрено сохранение результата умножения в двух регистрах, например x86 результат операции imul двух 32-битных чисел сохраняется в EAX и EDX.
Терминологически неграмотно брать массивы из 8 элементов, если арифметика 32-битная, надо аккуратнее действовать.
uint16_t a_high = { 0xff, 0xff }; // 1-ое число 16-разрядное
uint16_t a_low = { 0xff, 0xff} ; // 2-ое число 16-разрядное
uint16_t a = { a_high, a_low }; // 1-ое число 32-разрядное
и
uint16_t b_high = { 0xff, 0xff }; // 3-ье число 16-разрядное
uint16_t b_low = { 0xff, 0xff} ; // 4-ое число 16-разрядное
uint16_t b = { b_high, b_low }; // 2-ое число 32-разрядное
Формула такая
a * b = 16^2 * a_high * b_high +
16 * (a_high * b_low + a_low * b_high) +
a_low * b_low
Произведение и сложение 16-разрядных реализовываем в функциях, а при программировании формулы их вызываем.
Нисколько, это классический подход с результом и отдельно переполнением, только его сложнее реализовывать, на мой взгляд. Использование carry даже оптимальнее, если речь идёт о реальных процессорах и нужно минимизировать число тактов для операции сложения и умножения.
p.s. реализация функции mul() не нравится, хотелось бы как-то покомпактнее её записать, но ничего в голову не пришло...
Solution 1
// Result and overflow
Сейчас буду пробовать через усечение
Работает, поэтому заключу, что всё работает.
взял бы случайное число и сложил бы его сам с собой 12345 раз.
Потом проверил на равенство результат и умножение.
В качестве нового слагаемого на вход сумматору подавать результат суммы, полученной на предыдущем шаге.
В варианте, который я предложил, Число 65535 размещается в массиве из двух элементов типа uint_16t, a[0]=0xff, a[1]=0xff
сложение, это b[0]=a[0]+a[0]=0x1fe, b[1]=a[1]+a[1]=0x1fe, и переносов: b[1] = (a[0] >> 8) + b[0] и сохранить b[2] перенос из b[1].
пробую написать сложение, но никак не получается.
Вот код
Вывод
sum: 1 fffe
p.s.
вообще думал мне задание на ООП кинут, а тут чистый сишник, как увидел, так сразу плохо стало
само задание сдал, но спортивный интерес остался, хочу через битовые сдвиги запилить
прогуглил интернет и что странно, эта тема как-то криво раскрыта в сети, как бы код есть, но не тот, который хотелось бы увидеть.
Вывод:
mul: fe ff01
Правильно работает, хм