Name | Precision | Sign | Fractional | Exponent | 10進換算桁数 |
---|---|---|---|---|---|
Half | 16 | 1 | 10 | 5 | 3.31 |
Single | 32 | 1 | 23 | 8 | 7.22 |
Double | 64 | 1 | 52 | 11 | 15.95 |
s e f s:sign, e:exponent(8 bits), f: fraction (23 bits) 3 32222222 22211111111110000000000 1 09876543 21098765432109876543210exponent は8bit幅で [0, 255] の値を取るが 0と255の場合は特別な意味となる。 [1,254]の場合は数値を表すが+127のオフセットがあるため実際は[-126, 127] の値を表すことになる。
種類 | exponent | fraction |
---|---|---|
ゼロ | 0 | 0 |
非正規化数 | 0 | 0以外 |
正規化数 | 1-254 | 任意 |
無限大 | 255 | 0 |
NaN | 255 | 0以外の任意 |
.single 1.343, 4.343e20, -0.4343, -0.4444e-10 .double -4.24322322332e-10, 3.141592653589793
32個の128 bitsレジスタ V0, ..., V31 があり、 ARM FPU と NEON coprocessor はこれらのレジスタを共有している。 Vnレジスタの下位64,32, 16bit部分にアクセスするには、Dn, Sn, Hn (D0, ..., D31, S0, ..., S31, H0, ..., H31)というレジスタ名を用いる。
FPUでは64bit以下の浮動小数点数しか扱えない。128bit浮動小数点数を扱うためには NEON Coprocessor が必要となる。
NEON Coprocessor は 128 bit の整数をレジスタに入れることができるが、その場合は Qn (Q0, ..., Q31)というレジスタ名を用いる。 128bitレジスタ全体をスタックに push したり pop したりするためにも Qn というレジスタ名でアクセスする必要がある。
STP Q8, Q9, [SP, #-32]! STR Q10, [SP, #-16]! ... LDR Q10, [SP], #16 LDP Q8, Q9, [SP], #32
LDR X1, =fp1 LDR S4, [X1] // fp1 LDR D5, [X1, #4] // fp2 STR S4, [X1] // fp1 STR D5, [X1, #4] // fp2 .data fp1: .single 3.14159 fp2: .double 4.3341 fp3: .single 0.0 fp3: .double 0.0
FMOV 命令を使って、CPUの整数レジスタとFPUのレジスタの間、 および、FPUの2つのレジスタの間でデータをコピーできる。 一般には、レジスタは同じサイズでなければならないが、 半精度 H レジスタは大きな整数レジスタとの間でデータをコピーできる。
FMOV H1, W2 FMOV W2, H1 FMOV S1, W2 FMOV X1, D2 FMOV E2, E3
乗算と加算のような多少の拡張はあるが、4種類の基本的な命令がある。 また、2乗根のような特別の関数もある。
各命令は H, S, D レジスタのどれでも使うことができる。 ([自分へのメモ]おそらくレジスタ幅は同じである必要がある。) 下に命令の抜粋を示す。
FADD Dd, Dn, Dm // Dd = Dn + Dm FADD Sd, Sn, Sm FADD Hd, Hn, Hm FSUB Dd, Dn, Dm // Dd = Dn - Dm FMUL Dd, Dn, Dm // Dd = Dn * Dm FDIV Dd, Dn, Dm // Dd = Dn / Dm FMADD Dd, Dn, Dm, Da // Dd = Da + Dn * Dm FMSUB Dd, Dn, Dm, Da // Dd = Da - Dn * Dm FNEGB Dd, Dn // Dd = -Dn FABS Dd, Dn // Dd = abs(Dn) FMAX Dd, Dn, Dm // Dd = max(Dn, Dm) FMIN Dd, Dn, Dm // Dd = min(Dn, Dm) FSQRT Dd, Dn // Dd = min(Dn)
2つの点の座標 ($x_1$, $y_1$), ($x_2$, $y_2$) が単精度で与えられた時、 その間の距離 $d = \sqrt ((y_2 - y_1)^2 + (x_2 - x_1)^2)$ を求めるコード。
// distance between two points in single precision floating-point. // // Inputs: // X0 - pointer to the 4 FP numbers x1, y1, x2, y2 // Outputs: // X0 - the length as single precision .global distance distance: STR LR, [SP, #-16]! // save Link Register LDP S0, S1, [X0], #8 LDP S2, S3, [X0] FSUB S4, S2, S0 // s4 = x2 - x1 FSUB S5, S3, S1 // s5 = y2 - y1 FMUL S4, S4, S4 // s4 = s4^2 FMUL S5, S5, S5 // s5 = s5^2 FADD S4, S4, S5 // s4 = s4 + s5 FSQRT S4, S4 // s4 = sqrt(s4) FMOV W0, S4 // return value LDR LR, [SP], #16 // restore Link Register RET
// Main program .global main .eqn N, 3 // number of points main: STP X19, X20, [SP, #-16]! STR LR, [SP, #-16]! LDR X20, =points MOV W19, #N loop: MOV X0, X20 BL distance FMOV S2, W0 // S2 <-- return value FCVT D0, S2 // convert double precision FMOV X1, D0 LDR X0, =prtstr BL printf ADD X20, X20, #(4*4) // 4 points each 4 byte SUBS W19, W19, #1 B.NE loop MOV X0, #0 // return code LDR LR, [SP], #16 LDP X19, X20, [SP], #16 RET .data points: .single 0.0, 0.0, 3.0, 4.0 // 1st: x1, y1, x2, y2 .single 1.3, 5.4, 3.1, -1.5 // 2nd: x1, y1, x2, y2 .single 1.323e10, -1.2e-4, 34.55, 5454.234 // 3rd: x1, y1, x2, y2 prtstr: .asciz "Distance = %f\n"
半精度 Hn, 単精度 Sn, 倍精度 Dn の間の変換を行う FPU 命令。
FCVT Dd, Sm FCVT Sd, Dm FCVT Sd, Hm FCVT Hd, Sm
整数を浮動小数に変換する命令。
SCVTF Dd, Xm // Dd = signed integer from Xm UCVTF Sd, Wm // Sd = unsigned integer from Wm
浮動小数を整数に変換する命令は、round handled 丸め方によっていくつかある。
FCVTAS Wd, Hn // signed, round to nearest FCVTAU Wd, Sn // unsigned, round to nearest FCVTMS Xd, Dn // signed, round towards minus infinity FCVTMU Xd, Dn // unsigned, round towards minus infinity FCVTPS Xd, Dn // signed, round towards positive infinity FCVTPU Xd, Dn // unsigned, round towards positive infinity FCVTZS Xd, Dn // signed, round towards zero FCVTZU Xd, Dn // unsigned, round towards zero
floating-point 命令のほとんどは 'S'が付加されないので、条件フラグを変化させない。 条件フラグを変化させるには FCMP 命令を使う。
FCMP Hd, Hm FCMP Hd, #0.0 FCMP Sd, Sm FCMP Sd, #0.0 FCMP Dd, Dm FCMP Dd, #0.0
Hn同士や、Sn同士、Dn同士を比較できる。 また、#0.0という即値と比較できる(zero register がないから必要である)。