ぐだぐだ低レベルプログラミング(56) RISC-V、浮動小数点数のClassify命令

Joseph Halfmoon

また、メンドクセー命令が出てきました。浮動小数点数の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 だね。知らんけど。

無限大および NaN

このお陰をもって書き下したテストケースが以下に。

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)

大したことはしていないのだけれど、メンドイ奴をやっつけると何か達成感がありますな。ホントか?ちょろいな、自分。

ぐだぐだ低レベルプログラミング(55) RISC-V、sign-injection命令「群」へ戻る

ぐだぐだ低レベルプログラミング(57) RISC-V、浮動小数点数のロード/ストア命令 へ進む