ARMv8.3-CompNum FCMLA命令の動作
FCMLA命令の動作について書く。
FCMLA <Zda>.<T>, <Pg>/M, <Zn>.<T>, <Zm>.<T>, <const>
複素数の乗算はFCMLA命令2つで書ける。 ベクトルレジスタには複数の複素数を格納できる。 偶数番目が実部で、奇数番目が虚部。 ベクトルレジスタが512ビットの場合、倍精度(double, 64ビット)の複素数を4つ格納できる。
この前提のもと、FCMLA命令の動作は以下の表のようになる。
const | 実部 | 虚部 |
---|---|---|
0 | Re(Zda)+=Re(Zn)*Re(Zm) | Im(Zda)+=Re(Zn)*Im(Zm) |
90 | Re(Zda)+=Im(Zn)*(-Im(Zm)) | Im(Zda)+=Im(Zn)*Re(Zm) |
180 | Re(Zda)+=Re(Zn)*(-Re(Zm)) | Im(Zda)+=Re(Zn)*(-Im(Zm)) |
270 | Re(Zda)+=Im(Zn)*Im(Zm) | Im(Zda)+=Im(Zn)*(-Re(Zm)) |
確認してみる。
#include <stdio.h> #include <arm_sve.h> int main() { double a[8]; double b[8]; double c[8]; for (int i=0; i<8; i++) a[i] = i % 2 == 0 ? -i : i; for (int i=0; i<8; i++) b[i] = i * 2.0; for (int i=0; i<8; i++) c[i] = 0.0; svbool_t pg = svptrue_b64(); svfloat64_t avec = svld1(pg, a); svfloat64_t bvec = svld1(pg, b); svfloat64_t cvec = svld1(pg, c); puts("*** rotate == 0 ***"); svfloat64_t result = svcmla_x(pg, cvec, avec, bvec, 0); svst1(pg, c, result); for (int i=0; i<8; i++) printf("c[%d]=%f\n", i, c[i]); puts("check:"); for (int i=0; i<8; i+=2) { // re c[i] = a[i] * b[i]; // im c[i+1] = a[i] * b[i+1]; } for (int i=0; i<8; i++) printf("c[%d]=%f\n", i, c[i]); puts("*** rotate == 90 ***"); result = svcmla_x(pg, cvec, avec, bvec, 90); svst1(pg, c, result); for (int i=0; i<8; i++) printf("c[%d]=%f\n", i, c[i]); puts("check:"); for (int i=0; i<8; i+=2) { // re c[i] = a[i+1] * -b[i+1]; // im c[i+1] = a[i+1] * b[i]; } for (int i=0; i<8; i++) printf("c[%d]=%f\n", i, c[i]); puts("*** rotate == 180 ***"); result = svcmla_x(pg, cvec, avec, bvec, 180); svst1(pg, c, result); for (int i=0; i<8; i++) printf("c[%d]=%f\n", i, c[i]); puts("check:"); for (int i=0; i<8; i+=2) { // re c[i] = a[i] * -b[i]; // im c[i+1] = a[i] * -b[i+1]; } for (int i=0; i<8; i++) printf("c[%d]=%f\n", i, c[i]); puts("*** rotate == 270 ***"); result = svcmla_x(pg, cvec, avec, bvec, 270); svst1(pg, c, result); for (int i=0; i<8; i++) printf("c[%d]=%f\n", i, c[i]); puts("check:"); for (int i=0; i<8; i+=2) { // re c[i] = a[i+1] * b[i+1]; // im c[i+1] = a[i+1] * -b[i]; } for (int i=0; i<8; i++) printf("c[%d]=%f\n", i, c[i]); }
$ aarch64-linux-gnu-gcc-10 fcmla.c -march=armv8.3-a+sve -static $ qemu-aarch64 -cpu max,sve-max-vq=4 a.out *** rotate == 0 *** c[0]=0.000000 c[1]=0.000000 c[2]=-8.000000 c[3]=-12.000000 c[4]=-32.000000 c[5]=-40.000000 c[6]=-72.000000 c[7]=-84.000000 check: c[0]=0.000000 c[1]=0.000000 c[2]=-8.000000 c[3]=-12.000000 c[4]=-32.000000 c[5]=-40.000000 c[6]=-72.000000 c[7]=-84.000000 *** rotate == 90 *** c[0]=-2.000000 c[1]=0.000000 c[2]=-18.000000 c[3]=12.000000 c[4]=-50.000000 c[5]=40.000000 c[6]=-98.000000 c[7]=84.000000 check: c[0]=-2.000000 c[1]=0.000000 c[2]=-18.000000 c[3]=12.000000 c[4]=-50.000000 c[5]=40.000000 c[6]=-98.000000 c[7]=84.000000 *** rotate == 180 *** c[0]=0.000000 c[1]=0.000000 c[2]=8.000000 c[3]=12.000000 c[4]=32.000000 c[5]=40.000000 c[6]=72.000000 c[7]=84.000000 check: c[0]=-0.000000 c[1]=-0.000000 c[2]=8.000000 c[3]=12.000000 c[4]=32.000000 c[5]=40.000000 c[6]=72.000000 c[7]=84.000000 *** rotate == 270 *** c[0]=2.000000 c[1]=0.000000 c[2]=18.000000 c[3]=-12.000000 c[4]=50.000000 c[5]=-40.000000 c[6]=98.000000 c[7]=-84.000000 check: c[0]=2.000000 c[1]=-0.000000 c[2]=18.000000 c[3]=-12.000000 c[4]=50.000000 c[5]=-40.000000 c[6]=98.000000 c[7]=-84.000000
0.0
と-0.0
でずれているところがあるのはなぜ?