実際の計算では、積の和を求めるという処理が多く出てくる。例えば、売り上げを計算する場合は、それぞれの品目の単価×数量を計算し、それらの総和を取るという処理となる。また、科学技術計算で良く出てくる行列の積の計算も、行ベクトルと列ベクトルの内積を求める積和計算である。そして、アナログ信号をディジタル処理するディジタルフィルタでも入力に係数を掛けて総和を取るという積和計算が中心的な処理となる。

ということで、これを積と和という2回の演算を行うのではなく、A×B+Cという3入力の演算としてまとめてやってしまう方が効率が良いという考え方が出てくる。この演算を行う機構が積和演算器である。

積和演算は命令コードの設計に負担

積和演算命令は、入力となる3つのレジスタと出力レジスタの、合計4つのレジスタ指定フィールドを必要とするので、命令のエンコーディングとしてはかなりの負担である。例えば、PlayStation 3のために開発されたCELLプロセサのSPEは128個の汎用レジスタを持っているので、各レジスタ指定に7ビットを必要とし、これが4つあるので、レジスタ指定だけで32ビット長の命令の内の28ビットを使ってしまう。結果として、命令の種別を指定するビットは4ビットしか残らない。更に、C+A×B→DとC-A×B→Dの二つのバリエーションの命令があるので、積和命令だけで、全命令スペースの1/8を使っている。

通常のRISCプロセサではレジスタ数は32であるので、4つのレジスタ指定に必要なビット数は20ビットであるが、それでも大きな負担であるので、命令設計の当初から積和命令のサポートを考慮していないと採用は難しく、x86には積和命令は無い。しかし、PowerPC、Itanium、富士通のSPARC64などでは、積和命令のサポートを行い、積和演算が多用される計算の性能を向上している。

積和演算器では巨大なシフタとアダーを使う

積和演算では、A×Bの106ビット長の積に対して丸めを行わず、Cと桁合わせするためのシフトを行って、加算を行うというのが一般的なやり方である。こうすれば積の結果を丸めるための1サイクルを節約できるし、106ビット長の積をそのまま使うので、計算精度も高い。

しかし、精度が高いために、積の結果を丸めて、それにCを加算した場合とは結果が異なるケースがあり、IEEE 754に完全に準拠した演算結果が必要な場合は、積和演算を使ってはならない。ということで、積和演算をサポートしているプロセサのコンパイラは、一般的に、積和演算を使うかどうかをコンパイルフラグで指定することが出来るようになっている。

このように、現状では積和演算はママコの扱いであるが、改定を審議中のIEEE 754r規格では、積和演算が追加される予定であると言われている。図21に積和演算器のブロックダイヤグラムを示す。

図21:浮動小数点積和演算器の構成。

右側の上の部分は図20に示した浮動小数点乗算器と同じであるが、CPAの部分で桁あわせを行ったオペランドCが足し込まれている。そして加算で桁落ちが生じる可能性があるので、リーディングゼロをカウントして正規化するための左シフタが入っている。オペランドCは、アダー1で作られた積の指数部から、アダー2でオペランドCのEXPを引き、桁合わせのためのシフト量を求める。

加算器の場合は、指数部の値を比較してオペランドの入れ替えを行ったが、積和演算器の場合は、入れ替えが出来ない。もちろん、入れ替えは不可能ではないが、積のパイプラインに入れ替えのためのマルチプレクサを入れると遅延時間が増えるので、性能の点で好ましくない。このため、オペランドCの方をA×Bに桁合わせするというシフトを行う。両者の指数の差は正になる場合も負になる場合もあるので、ここでは左右のどちらにもシフトできるシフタが必要になる。

図22:積和演算器の加算は158ビットの幅が必要となる。

そして、Cの指数がA×Bの指数より52大きい場合には、図22のようにA×Bの最上位のビットの位置にCの最下位のビットが来るので、158ビットの加算器が必要になる。なお、指数部の値に53以上の差がある場合は重なりが無いので、Cを結果として出力し、ガードビット以下のビットをA×Bの積から生成して補えば良く、長い加算は必要ない。一方、図中に灰色で書いたCの指数が小さい場合は、単純に桁あわせしたCを足し込めば良い。

このようにCPAのビット長が大きくなることと、正規化用のシフタが入ることから、積和演算器では、Booth Encoderまでをパイプランの最初の段、Wallace Treeを2~3段のパイプステージ、CPA、正規化シフタ、丸めをそれぞれ1段のパイプステージとして、6~7段のパインプラインで構成するのが一般的であるが、クロックがあまり高くない場合は、 より多くの処理を1段に詰め込んで、より短いパイプラインで実装している例もある。