ARM NEONの使い方 減算編
年内に終わるかな?
今回は減算編です
減算 (通常の減算、符号拡張付き減算、飽和付き減算)
加算編で紹介したものとほぼ変わらないので、まとめて紹介
サンプル
符号付き16bit整数のベクタvaとvbの引き算を、先ほど紹介した3つの方法でやってみます
#include <stdio.h> #include <stdint.h> #include <arm_neon.h> int main() { int16_t a[4] = { 1, -1, 1, -2 }; int16_t b[4] = { 1, -1, -32767, 32767 }; int16x4_t va = vld1_s16(a); int16x4_t vb = vld1_s16(b); int16x4_t vc = vsub_s16(va, vb); int32x4_t vcl = vsubl_s16(va, vb); int16x4_t vcq = vqsub_s16(va, vb); printf("vsub_s16\n"); int16_t c[4]; vst1_s16(c, vc); for (int i = 0; i < 4; i++) printf("c[%d]: %d\n", i, c[i]); printf("\nvsubl_s16\n"); int32_t cl[4]; vst1q_s32(cl, vcl); for (int i = 0; i < 4; i++) printf("cl[%d]: %d\n", i, cl[i]); printf("\nvqsub_s16\n"); int16_t cq[4]; vst1_s16(cq, vcq); for (int i = 0; i < 4; i++) printf("cq[%d]: %d\n", i, cq[i]); return 0; }
実行結果
vsub_s16 c[0]: 0 c[1]: 0 c[2]: -32768 c[3]: 32767 vsubl_s16 cl[0]: 0 cl[1]: 0 cl[2]: 32768 cl[3]: -32769 vqsub_s16 cq[0]: 0 cq[1]: 0 cq[2]: 32767 cq[3]: -32768
3、4番目のレーンの引き算の結果がそれぞれ
- vsub_s16()ではオーバーフロー
- vsubl_s16()では32bitに拡張
- vqsub_s16()では最大値/最小値で飽和
となっています
符号なしベクタの減算について
符号なし16bit整数のベクタvaとvbの減算を考えます
このときvsub_u16()を使ってしまうと、戻り値も符号なし16bitなので
va[i] >= vb[i]の場合は正しい結果が得られるのですが
va[i] < vb[i]の場合はオーバーフローが発生してしまいます
正しい結果を得るためには、ベクタを符号付き32bitに拡張する必要がありますが、
これを1回で実現してくれるNEON命令はなさそうです
そこで1つ思いついたのが、vsubl_u16()を使用して減算結果を符号なし32bitに拡張し
vreinterpretq_s32_u32()で符号付き32bitとして解釈する方法です
サンプル
符号なし16bit整数のベクタvaとvbの引き算を、vsub_u16()を使った方法と
vsubl_u16() & vreinterpretq_s32_u32()を使った方法でやってみます
#include <stdio.h> #include <stdint.h> #include <arm_neon.h> int main() { uint16_t a[4] = { 1, 1, 1, 1 }; uint16_t b[4] = { 0, 1, 2, 65535 }; uint16x4_t va = vld1_u16(a); uint16x4_t vb = vld1_u16(b); uint16x4_t vc = vsub_u16(va, vb); uint32x4_t vc_u32 = vsubl_u16(va, vb); int32x4_t vc_s32 = vreinterpretq_s32_u32(vc_u32); printf("vsub_u16\n"); uint16_t c[4]; vst1_u16(c, vc); for (int i = 0; i < 4; i++) printf("c[%d]: %d\n", i, c[i]); printf("\nvsubl_u16 and vreinterpretq_s32_u32\n"); int c_s32[4]; vst1q_s32(c_s32, vc_s32); for (int i = 0; i < 4; i++) printf("c_s32[%d]: %d\n", i, c_s32[i]); return 0; }
vsub_u16 c[0]: 1 c[1]: 0 c[2]: 65535 c[3]: 2 vsubl_u16 and vreinterpretq_s32_u32 c_s32[0]: 1 c_s32[1]: 0 c_s32[2]: -1 c_s32[3]: -65534
vsubl_u16() & vreinterpretq_s32_u32()を使った方法で、一応正しい結果が得られました
やり方として良いのかわかりませんが…
次回
次回は乗算編です
ARM NEONの使い方 乗算編