前回は柄にもなく浮動小数点の丸めを説明したところで力尽きてしまいました。今回は前回の残り、浮動小数点の例外についてです。こういうメンドイものがあるので浮動小数点演算は後回しにしたくなるのですが、実際に浮動小数点演算命令を動かすにあたっては避けていられません。それにしても面倒くさいよ。
※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら
「例外」といってトラップ発生するわけでもない
さて前回も引用しましたが、参照しておりますドキュメントは、RISC-V InternationalのRISC-V Specificationsに掲載されている以下のマニュアルです。
The RISC-V Instruction Set Manual
Volume I: Unprivileged ISA
Document Version 20191213
上記のマニュアルを読んでいると、さらっと書いてあります。1か所引用させていただきます。
The base RISC-V ISA does not support generating a trap on the setting of a floating-point exception flag.
「例外フラグ」といったら、それが立ったらトラップが発生する(マスクはできるかもしれないケド)というのが世に蔓延る「例外」じゃないかと思います。しかしRISC-Vでは、ベースのISAじゃ発生させなくて良い、ということみたいです。「必ずトラップ発生させよ」とすると実装面での負担が大きくなる(スーパースカラーみたいなことをするときにメンドい)ので、命令セットISAであるRISC-Vの基本規格ではそこまで要求しなかったみたいです。よって浮動小数点の例外フラグは実行した一連の浮動小数点演算命令の「どこかで」例外が発生したことを示すけれども、フラグを読みにいかなければ頬かむりして済ませることもできてしまう、と。
浮動小数点例外の背景知識
浮動小数点演算の例外の種類を調べるにあたっては、NaN(ナン、といってインドカレー屋さんのアレではありませんよ)とかデノーマル(アブノーマルでなくてよかった)とか浮動小数点業界関係者には説明の必要のない、しかし、私のような門外漢には?な存在を抜きには語れませぬ。
単精度浮動小数点数フォーマットにおける NaN (Not a Number)の表現を説明した図を冒頭のアイキャッチ画像に掲げました。符号ビットが0で指数部がオール1、そして仮数部のMSB(隠れた最上位ビットでなく、実体のある最上位ビット)が1でそれ以外が0というのがNaNです。NaNは数ではないものの表現で、0を0で割るとか、無限大を無限大で割るとか、どうしてよいか分からないときに登場してくる「表現」です。
当然ながらひとたびNaNが登場すると、それが絡む全ての演算は意味を持ちません。x87系の流れをくむx86のFPUではNaNの伝播が起こるのが普通だと思います。RISC-Vでは、NaNの伝播は「オプショナル」扱いみたいです。例外フラグに反映するのは「スタンダード」ですが、NaNの伝播は「ノンスタンダード」なオペレーティング・モードでなら行ってもよい、ということみたいです。
また、NaNで1が立っていた仮数部のMSBに0を置くと(実体のある化数部はオール0)無限大を表すこととなります。その際には、符号ビットに1(マイナス)を立ててもよく、負の無限大も表現できます。勿論、例外フラグには無限大も関係してきます。
他にもいろいろありそうですが、具体例にぶつかったらまた調べて記すことにし、先を急ぎたいと思います。このところ前置きばかり長くて、なかなか実際にK210を動かせてないです。
浮動小数点例外の種類
前回使用した fcsr レジスタの図を再掲載させていただきます。
例外ビット(水色)とそれの発生条件(今のところ、RISC-V実機で確かめたわけでなく、仕様を読んで個人的に理解した範囲で書いているので鵜呑みにしないでくだされ)を以下に記します。
NV
無効演算。上述のNaNが発生するような、あるいは、NaNが絡んだ演算など。
DZ
有限でゼロでない数に対するゼロによる割り算。
OF
オーバーフロー。浮動小数点フォーマット上表現可能な最大数を超えた。
UF
表現可能な最小の正規化数(ノーマル数)を下回った。
NX
Inexact。演算結果が「不正確」。演算結果が「ピタリの数値」にならず丸めが発生すれば不正確となるはず。じゃ、何時もじゃん!
実際に上記のフラグ群の動作を確かめるのは、それらしいオペランドを与えて計算してみればよいはず。次回からの実命令のエクササイズでは、ときどき例外フラグの状態も観察していきたいと思います。
まあ、調べものは見切り発車で実験にGo。