また、メンドクセー命令が出てきました。浮動小数点数のClassify命令です。無限大、デノーマル、NaNといった訳ありのモノドモを識別する命令。勿論、普通の(ノーマル)な数はノーマルと分類します。浮動小数点素人の私にはNaNの2種類あるビットパターンなど皆目見当がつかないです。
※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら
※以下は単精度浮動小数点演算命令について述べています。なお実機動作は、64bit RISC-V搭載、Kendryte K210上で行っています。
Classify type 命令
命令セット的には、3オペランドの命令として定義されています。
fclass.s rd, rs1, rs2
浮動小数レジスタrs1に格納されているfloatの値を以下のように分類し、それを示すビットを立てた整数を、整数レジスタrdに格納します。
-
- ビット0 負の無限大
- ビット1 負のノーマル数
- ビット2 負のデノーマル数
- ビット3 負の0
- ビット4 正の0
- ビット5 正のデノーマル数
- ビット6 正のノーマル数
- ビット7 正の無限大
- ビット8 シグナル型のNaN
- ビット9 クワイエット型のNaN
正直、10種もではなく、10種で収まって良かった、という感じです。ホント、浮動小数点はメンドクセー奴ですな。
オペコード的には3レジスタを引数にとれるのですが、rs2は使われていないように見えます。手元のK210用の処理系では rs2を指定するとエラーになりました。表記法としては、rs2を無視して
fclass.s rd, rs1
とすれば通りました。
被テスト関数のソース
分類結果の整数を一つ、その中に立っているビットは一つだけの命令なので、関数内部で結果ビットを識別するようにしました。コードは簡単です。
void tst_Fclass(uint32_t tnum, uint32_t arg1) { uint32_t rd; TestFloat f1S; rd = 0; f1S.u32Dat.LowW = arg1; asm volatile("fmv.w.x f1, %[Rs1]\n\t" "fclass.s %[Rd], f1\n\t" : [Rd] "=r" (rd) : [Rs1] "r" (f1S.u32Dat.LowW) : ); Serial.printf("TEST-fclass.s #%u\r\n", tnum); Serial.printf(" HEX: %08x\r\n", f1S.u32Dat.LowW); Serial.printf(" FLOAT: %e\r\n", f1S.fDat); Serial.printf(" CLASS: "); switch (rd) { case 0x001: Serial.printf("-INF"); break; case 0x002: Serial.printf("-NORMAL"); break; case 0x004: Serial.printf("-DENORMAL"); break; case 0x008: Serial.printf("-ZERO"); break; case 0x010: Serial.printf("+ZERO"); break; case 0x020: Serial.printf("+DENORMAL"); break; case 0x040: Serial.printf("+NORMAL"); break; case 0x080: Serial.printf("+INF"); break; case 0x100: Serial.printf("NaN(SIGNAL)"); break; case 0x200: Serial.printf("NaN(QUIET)"); break; default: Serial.printf(" Unknown Flag=%08x", rd); } Serial.printf("\r\n\r\n"); }
テストケース
今回問題なのはテストケースの方です。アセンブラレベルのテストなので、普通じゃない奴らのビットパターンを綴る必要があります。FPUのプロの人なら、無意識にでもビットパターンを綴れるのじゃないかと思いますが、素人の私には無理です。だいたいNaNって2種類もあったんだけという感じです。IBM社の XL Fortran for AIXを説明している以下のページにお世話になりました。やっぱ Fortran だね。知らんけど。
このお陰をもって書き下したテストケースが以下に。
tst_Fclass(0, 0xFF800000); //-INF tst_Fclass(1, 0xC0000000); //-NORMAL tst_Fclass(2, 0x80400000); //-DENORMAL tst_Fclass(3, 0x80000000); //-ZERO tst_Fclass(4, 0x00000000); //+ZERO tst_Fclass(5, 0x00400000); //+DENORMAL tst_Fclass(6, 0x40000000); //+NORMAL tst_Fclass(7, 0x7F800000); //+INF tst_Fclass(8, 0x7F800001); //NaN(SIGNAL) tst_Fclass(9, 0x7FC00000); //NaN(QUIET)
実機実行結果
テストケースさえ出来てしまえば、後は走らせるだけ。結果が以下に。
TEST-fclass.s #0 HEX: ff800000 FLOAT: -inf CLASS: -INF TEST-fclass.s #1 HEX: c0000000 FLOAT: -2.000000e+00 CLASS: -NORMAL TEST-fclass.s #2 HEX: 80400000 FLOAT: -5.877472e-39 CLASS: -DENORMAL TEST-fclass.s #3 HEX: 80000000 FLOAT: -0.000000e+00 CLASS: -ZERO TEST-fclass.s #4 HEX: 00000000 FLOAT: 0.000000e+00 CLASS: +ZERO TEST-fclass.s #5 HEX: 00400000 FLOAT: 5.877472e-39 CLASS: +DENORMAL TEST-fclass.s #6 HEX: 40000000 FLOAT: 2.000000e+00 CLASS: +NORMAL TEST-fclass.s #7 HEX: 7f800000 FLOAT: inf CLASS: +INF TEST-fclass.s #8 HEX: 7f800001 FLOAT: nan CLASS: NaN(SIGNAL) TEST-fclass.s #9 HEX: 7fc00000 FLOAT: nan CLASS: NaN(QUIET)
大したことはしていないのだけれど、メンドイ奴をやっつけると何か達成感がありますな。ホントか?ちょろいな、自分。