前回は単精度と倍精度の間のフォーマット変換を行うFCVT命令でした。同じ命令が丸めモードの設定で動作が変わるので厄介でした。今回のFCVTxy命令群(xyのところはいろいろ)は、浮動小数点数を整数に変換するもの。丸めモードフラグには関係なしと喜んだら、かえって組み合わせが増えているでないの?もうメンドくてやりきれない?
※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら
※実機確認には以下を使用しております。
-
- Raspberry Pi 4 model B、Cortex-A72コア(ARMv8-A)
- Raspberry Pi OS (64bit) bullseye
- gcc (Debian 10.2.1-6) 10.2.1 20210110
ARMv8もいろいろレベルがあり、Arm Cortex-A72はARMv8の中でもベーシックな(命令数の少ない)ARMv8p0です。
※A64の最新のマニュアルは以下でダウンロード可能です。
Arm Architecture Reference Manual for A-profile architecture
浮動小数点数から整数への変換命令群
浮動小数点レジスタの中の浮動小数点数(スカラー)を整数レジスタの中の整数に変換する命令群には、以下のような組合わせがあります。
-
- ソース、単精度/倍精度(半精度)
- デスティネーション、W(32ビット)レジスタ、X(64ビット)レジスタ
- 符号、あり、なし
- 丸め
ARMv8p2以降では、半精度浮動小数のサポートがあるので、組み合わせの数はさらに倍増してしまうのです。ターゲット機はARMv8p0なので半精度はありません。ホントよかった。
その組み合わせをまとめると以下の表のようになります。DesitinationのWは32ビット整数、Xは64ビット整数、SourceのSは単精度浮動小数、Dは倍精度浮動小数です。組み合わせとしては倍精度浮動小数を32ビット整数に変換するのもアリです。
また前回のFCVTはFPCRレジスタのRMビットで制御していた丸めモードは、命令自体にエンコードされてます。またRMビットとは異なる部分もあり。(※以下表は2024/7/3改定。)
INSN | Signed/Unsigned | Desitination | Source | Rounding Mode |
---|---|---|---|---|
FCVTAS | Signed | W/X | S/D | 最近接Away |
FCVTAU | Unsigned | W/X | S/D | 最近接Away |
FCVTMS | Signed | W/X | S/D | -∞ |
FCVTMU | Unsigned | W/X | S/D | -∞ |
FCVTNS | Signed | W/X | S/D | 最近接偶数 |
FCVTNU | Unsigned | W/X | S/D | 最近接偶数 |
FCVTPS | Signed | W/X | S/D | +∞ |
FCVTPU | Unsigned | W/X | S/D | +∞ |
FCVTZS | Signed | W/X | S/D | zero |
FCVTZU | Unsigned | W/X | S/D | zero |
FJCVTZS | Signed | W | D | zero |
毎度繰り返しますが、ARM64の命令多すぎ。とても覚えきれませぬ。丸めに関しては今回はRMビットには影響されないので前回のようにまったく同じ命令をモードを変えて試行してみる必要はないです。しかし、RMビットでは4種類しかなかった丸めモードが5種類に。なんか増えてないかい?それにFJCVTZSというのは何?JavaScript専用?流石Armじゃね。
とても一度で触ってみれないので、今回はUnsignedの変換命令のみ、ソース、デスティネーションの組み合わせも限って実習してみます。それに丸めモードも、プラス、マイナス無限大は端折って、「最近接away」と「最近接偶数」、「zero」のみ比べてみます。
今回実験のアセンブリ言語関数
今回も例によって関数プロローグもエピローグもない、手抜きな被テストアセンブラ関数群が以下に。
.globl fcvtauWS, fcvtauXS, fcvtauWD, fcvtauXD, fcvtzuWD, fcvtnuWD, readfpcr, writefpcr .text .balign 4 fcvtauWS: fcvtau w0, s0 ret fcvtauXS: fcvtau x0, s0 ret fcvtauWD: fcvtau w0, d0 ret fcvtauXD: fcvtau x0, d0 ret fcvtzuWD: fcvtzu w0, d0 ret fcvtnuWD: fcvtnu w0, d0 ret readfpcr: mrs x0, fpcr ret writefpcr: msr fpcr, x0 ret
C言語記述のmain関数
符号無に限っても組み合わせが多いので、かなり端折ってます。まず「最近接away」命令で、デスティネーションとソースの組み合わせを一通り動かした後(これとて被試験機がARMv8p0だということで単精度が無いので大分救われてます)、同じ数値に対して、3つの丸めモードの違いを見てます。
#include <stdio.h> #include <stdint.h> extern uint32_t fcvtauWS(uint32_t, float); extern uint64_t fcvtauXS(uint64_t, float); extern uint32_t fcvtauWD(uint32_t, double); extern uint64_t fcvtauXD(uint64_t, double); extern uint32_t fcvtzuWD(uint32_t, double); extern uint32_t fcvtnuWD(uint32_t, double); extern uint64_t readfpcr(uint64_t); extern uint64_t writefpcr(uint64_t); int main(void) { union { double resultD; uint64_t result64; } u64; union { float resultS; uint32_t result32; } u32; u32.result32 = ((uint32_t)readfpcr(0) >> 22) & 3; printf ("RM : %d\n", u32.result32); u32.result32 = fcvtauWS(0, 5.55f); printf ("fcvtauWS(0 5.55f): 0x%08x\n", u32.result32); u32.result32 = fcvtauWD(0, 5.55); printf ("fcvtauWS(0 5.55): 0x%08x\n", u32.result32); u64.result64 = fcvtauXS(0, 1.123456789012e12); printf ("fcvtauXS(0, 1.123456789012e12): %ld\n", u64.result64); u64.result64 = fcvtauXD(0, 1.123456789012e12); printf ("fcvtauXD(0, 1.123456789012e12): %ld\n", u64.result64); u32.result32 = fcvtauWD(0, 5.5); printf ("fcvtauWD(0 5.5): 0x%08x\n", u32.result32); u32.result32 = fcvtzuWD(0, 5.5); printf ("fcvtzuWD(0 5.5): 0x%08x\n", u32.result32); u32.result32 = fcvtnuWD(0, 5.5); printf ("fcvtnuWD(0 5.5): 0x%08x\n", u32.result32); u32.result32 = fcvtauWD(0, 6.5); printf ("fcvtauWD(0 6.5): 0x%08x\n", u32.result32); u32.result32 = fcvtzuWD(0, 6.5); printf ("fcvtzuWD(0 6.5): 0x%08x\n", u32.result32); u32.result32 = fcvtnuWD(0, 6.5); printf ("fcvtnuWD(0 6.5): 0x%08x\n", u32.result32); return 0; }
ビルドして実行
ビルドして実行した結果が以下に。
ピックアップした丸めモード3種のみ表にしてみると以下のとおりです。
丸めモード | 元値=5.5 | 元値=6.5 |
---|---|---|
最近接Away | 6 | 7 |
最近接偶数 | 6 | 6 |
zero | 5 | 6 |
整数と整数の真ん中の0.5の丸めを考える場合、EVEN、偶数方向丸め(上記では最近接偶数と表記)が「浮動小数点業界?」の定番じゃないかと思います。一方、最近接Awayを選択するとゼロから遠い側に変換してくれるみたいです。こちらの挙動の方が、慣れ親しんだ四捨五入ですかな。勿論、Zeroは、Zeroに近い方向の整数に丸め(切り捨て)てます。今回は符合無だけだったので、マイナスの数が扱えていないですが、マイナスも含めるともっと場合分けが増えるなあ、メンドイなあ。でも必要としている人はいる筈だし。もう1回くらい練習しておく?