ブースのアルゴリズム実装した記事が見つからない上にChatGPTに聞いても的外れなコードしか出来上がらないので自分で作ってみました.
符号付き二進数の乗算を効率的に行う手法です.加算器作ったならもちろん乗算器も作りたくなるよね?でも加算を何度もする乗算は計算に時間がかかってしまうので,ブースのアルゴリズムが編み出されました.
アルゴリズムは以下の通りです.
- 被乗数と乗数を二の補数表現で用意します.(今回は4bit分用意してみます)
- 被乗数については,符号を変えたものも用意しておきます.(もちろん補数表現を使用しますが,わかりやすいように以降は「-被乗数」とします)
- Aを「被乗数 + 00000」,Sを「-被乗数 + 00000」とします.
- P0を「0000 + 乗数 + 0」とします.
- 漸化的にPnを求めていきます.4bit同士の演算の場合,P4まで求めます.
- Pnの末尾2bitが「00」あるいは「11」の場合,Pnを算術右シフトしたものをPnとする
- 末尾が「01」の場合,PnにAを加算したうえで右シフトしたものをPn+1とする
- 末尾が「10」の場合,PnにSを加算したうえで右シフトしたものをPn+1とする
- P4の上位8桁が解となります.
詳しくはシフト演算とは?論理シフトと算術シフトの違いを調べよう!を参考にすると良いです.
別にCでも書けるんですけど,練習したいのでC++で書きます.
#include <iostream>
#include <vector>
constexpr int BIT_SIZE = 4;
constexpr int TABLE_SIZE = 2 * BIT_SIZE + 1;
using namespace std;
必要な変数は
- 被乗数
- -被乗数
- P0
で,必要な関数は,
- 整数を二進数に変換する関数
- 算術右シフトさせる関数
- 二進数加算させる関数
- Pn+1を求める関数
- 配列を表示させる関数
- 再帰的にPを求める関数
です.
まずは必要なメンバ変数を用意します.先ほどと同様にAは被乗数,Sは-被乗数,P0を乗数とします.
class BoothAlgorithm {
public:
private:
vector<int> A, S, P_0, Result;
binary[bit_size - 1 - i] = (x >> i) & 1;
とすることで,10進数の値を2進数にしたときにi桁目の値を対応する配列に格納していきます.
vector<int> to_binary(int x, int bit_size) {
vector<int> binary(bit_size, 0);
for (int i = 0; i < bit_size; i++) {
binary[bit_size - 1 - i] = (x >> i) & 1;
}
return binary;
}
算術シフトですので最上位ビットの処理に気をつけます.
void right_shift(vector<int>& vec) {
for (int i = vec.size() - 1; i > 0; --i) {
vec[i] = vec[i - 1];
}
vec[0] = vec[0] == 1 ? 1 : 0;
}
単純に要素ごとに加算させると,10進数として計算してしまうため,二進数としての繰り上がりを考慮する必要があります.
vector<int> add_vectors(const vector<int>& vec1, const vector<int>& vec2) {
vector<int> result(vec1.size());
int carry = 0;
for (size_t i = vec1.size(); i-- > 0;) {
int sum = vec1[i] + vec2[i] + carry;
result[i] = sum % 2;
carry = sum / 2;
}
return result;
}
- Pnの末尾2bitが「00」あるいは「11」の場合,Pnを算術右シフトしたものをPnとする
- 末尾が「01」の場合,PnにAを加算したうえで右シフトしたものをPn+1とする
- 末尾が「10」の場合,PnにSを加算したうえで右シフトしたものをPn+1とする
という条件をもとにシフトさせる関数を作ります.
vector<int> conditional_shift(const vector<int>& A, const vector<int>& S, const vector<int>& P) {
vector<int> result = P;
if (P[TABLE_SIZE - 2] == P[TABLE_SIZE - 1]) {
right_shift(result);
} else if (P[TABLE_SIZE - 2] == 0) {
result = add_vectors(result, A);
right_shift(result);
} else {
result = add_vectors(result, S);
right_shift(result);
}
return result;
}
あるとわかりやすいね
void print_array(const vector<int>& arr) {
for (int i = 0; i < arr.size(); i++) {
cout << arr[i] << " ";
}
cout << endl;
}
先ほど作ったconditional_shift()
を使ってP1∼P4まで再帰的に計算させます.
vector<int> booth_algorithm(vector<int>& A, vector<int>& S, vector<int>& P, int count = BIT_SIZE) {
if (count > 0) {
P = conditional_shift(A, S, P);
cout << "P_" << BIT_SIZE - count + 1 << ": ";
print_array(P);
return booth_algorithm(A, S, P, count - 1);
} else {
vector<int> Result(TABLE_SIZE - 1, 0);
for (int i = 0; i < TABLE_SIZE - 1; i++) {
Result[i] = P[i];
}
return Result;
}
}
コンストラクタでは,整数x, yを受け取り,2進数に変換しつつ適切な形式に変換して,各メンバ変数に代入していきます.また実行のために関数runを用意します.
public:
BoothAlgorithm(int x, int y) {
A.resize(TABLE_SIZE, 0);
S.resize(TABLE_SIZE, 0);
P_0.resize(TABLE_SIZE, 0);
Result.resize(TABLE_SIZE - 1, 0);
vector<int> binary_x = to_binary(x, BIT_SIZE);
vector<int> binary_minus_x = to_binary(-x, BIT_SIZE);
vector<int> binary_y = to_binary(y, BIT_SIZE);
for (int i = 0; i < BIT_SIZE; i++) {
A[i] = binary_x[i];
S[i] = binary_minus_x[i];
P_0[i + BIT_SIZE] = binary_y[i];
}
}
void run() {
cout << "P_0: ";
print_array(P_0);
Result = booth_algorithm(A, S, P_0);
cout << "Result: ";
print_array(Result);
}
あとはmain関数を書けば完成です.
int main() {
int x, y;
cin >> x >> y;
BoothAlgorithm booth(x, y);
booth.run();
return 0;
}
❯ cpp booth.cpp
-6 5
P_0: 0 0 0 0 0 1 0 1 0
P_1: 0 0 1 1 0 0 1 0 1
P_2: 1 1 1 0 1 0 0 1 0
P_3: 0 0 1 0 0 1 0 0 1
P_4: 1 1 1 0 0 0 1 0 0
Result: 1 1 1 0 0 0 1 0
❯ cpp booth.cpp
5 -6
P_0: 0 0 0 0 1 0 1 0 0
P_1: 0 0 0 0 0 1 0 1 0
P_2: 1 1 0 1 1 0 1 0 1
P_3: 0 0 0 1 0 1 0 1 0
P_4: 1 1 1 0 0 0 1 0 1
Result: 1 1 1 0 0 0 1 0
❯ cpp booth.cpp
4 -3
P_0: 0 0 0 0 1 1 0 1 0
P_1: 1 1 1 0 0 1 1 0 1
P_2: 0 0 0 1 0 0 1 1 0
P_3: 1 1 1 0 1 0 0 1 1
P_4: 1 1 1 1 0 1 0 0 1
Result: 1 1 1 1 0 1 0 0
実際に実行してみると上記のようになります.例えば,5×−6という演算をブースのアルゴリズムで解いた場合,P4=(11100010)2=(−30)10のように,確かに正しく計算できています.
また,−6×5でも同じような結果となっており,乗法の交換律も成り立っています.
4×−3も同様に正しく計算できています.
最後にコードの全体像を貼っておきます.
#include <iostream>
#include <vector>
constexpr int BIT_SIZE = 4;
constexpr int TABLE_SIZE = 2 * BIT_SIZE + 1;
using namespace std;
class BoothAlgorithm {
public:
BoothAlgorithm(int x, int y) {
A.resize(TABLE_SIZE, 0);
S.resize(TABLE_SIZE, 0);
P_0.resize(TABLE_SIZE, 0);
Result.resize(TABLE_SIZE - 1, 0);
vector<int> binary_x = to_binary(x, BIT_SIZE);
vector<int> binary_minus_x = to_binary(-x, BIT_SIZE);
vector<int> binary_y = to_binary(y, BIT_SIZE);
for (int i = 0; i < BIT_SIZE; i++) {
A[i] = binary_x[i];
S[i] = binary_minus_x[i];
P_0[i + BIT_SIZE] = binary_y[i];
}
}
void run() {
cout << "P_0: ";
print_array(P_0);
Result = booth_algorithm(A, S, P_0);
cout << "Result: ";
print_array(Result);
}
private:
vector<int> A, S, P_0, Result;
void right_shift(vector<int>& vec) {
for (int i = vec.size() - 1; i > 0; --i) {
vec[i] = vec[i - 1];
}
vec[0] = vec[0] == 1 ? 1 : 0;
}
vector<int> add_vectors(const vector<int>& vec1, const vector<int>& vec2) {
vector<int> result(vec1.size());
int carry = 0;
for (size_t i = vec1.size(); i-- > 0;) {
int sum = vec1[i] + vec2[i] + carry;
result[i] = sum % 2;
carry = sum / 2;
}
return result;
}
vector<int> to_binary(int x, int bit_size) {
vector<int> binary(bit_size, 0);
for (int i = 0; i < bit_size; i++) {
binary[bit_size - 1 - i] = (x >> i) & 1;
}
return binary;
}
vector<int> conditional_shift(const vector<int>& A, const vector<int>& S, const vector<int>& P) {
vector<int> result = P;
if (P[TABLE_SIZE - 2] == P[TABLE_SIZE - 1]) {
right_shift(result);
} else if (P[TABLE_SIZE - 2] == 0) {
result = add_vectors(result, A);
right_shift(result);
} else {
result = add_vectors(result, S);
right_shift(result);
}
return result;
}
void print_array(const vector<int>& arr) {
for (int i = 0; i < arr.size(); i++) {
cout << arr[i] << " ";
}
cout << endl;
}
vector<int> booth_algorithm(vector<int>& A, vector<int>& S, vector<int>& P, int count = BIT_SIZE) {
if (count > 0) {
P = conditional_shift(A, S, P);
cout << "P_" << BIT_SIZE - count + 1 << ": ";
print_array(P);
return booth_algorithm(A, S, P, count - 1);
} else {
vector<int> Result(TABLE_SIZE - 1, 0);
for (int i = 0; i < TABLE_SIZE - 1; i++) {
Result[i] = P[i];
}
return Result;
}
}
};
int main() {
int x, y;
cin >> x >> y;
BoothAlgorithm booth(x, y);
booth.run();
return 0;
}