Arm Neon Intrinsics Add Functions (Explained With C)

Did you know, Arm Neon Intrinsics have more than 10 different types of vector addition functions? The differences between: Vector Add, Vector Long Add, Vector Wide Add, Vector Rounding Halving Add, Vector Saturating Add, Pairwise Add and Add Across Vector might not appear obvious now, but by the end of this short post they will.

You might be thinking: “Why don’t I just go read the official Arm documentation instead of this post?”. Because I won’t use pretty graphics and fancy pseudo-code. Yeah that’s right, what we are going to do is look at some C code I wrote (and tested) that mimicks what the intrinsics functions do.

Vector Add (vadd)

The Vector Add function is exactly what you expect, here’s the 16x4 version:

Vector Long Add (vaddl)

The 16-bit version of Vector Long Add promotes the result of the addition to the next power of 2. In this case 32 bits.

Might not be obvious here, but a[i] and b[i] are promoted to int32_t.

Vector Wide Add (vaddw)

Notice that for this function, the size of parameter a is the next power of 2 when compared to parameter b. For this addition, parameter b gets promoted to 32 bits.

Vector Rounding Halving Add (vrhadd)

Although additions are used in the process, what you actually get is an element-wise rounded average.

Vector Saturating Add (vqadd)

Whether you call it clipping or saturating, this add function will limit the value to the maximum value that the output type allows.

Pairwise Add (vpadd)

A beast of an add function. Looking at the C code carefully, we see that it concatenates the additions of subsequent pairs of values in a and b.

Notice how a and b are concatenated into c

Add Across Vector (vaddv)

Returns the sum of vector a:

Conclusion

Turns out, I totally wrote this for myself. Hopefully it can also help you make optimal choices when picking Neon add functions.