Why shift by 16 and 32 instead of 15 and 31 for fixed-point?

The intrinsic fixed point functions like:

ARM M4 (can be used for q31 fixed-point):

SMULL RdLo, RdHi, Rn, Rm

ARM Helium (can be used for q15 fixed-point):

VMULH.S16 Qd,Qn,Qm

Both effectively "shift by" 32 and 16 respectively if used for fixed-point arithmetic.

However, since their values are signed, the effective ranges are only 31 and 15 bits respectively (the sign bit is just the sign bit), so the "taking the higher bits" part of their operation is undesirable, since, in effect, this is the same as returning half the result.

i.e. let's talk about 16bit q15.

In this case, the highest returnable value is 32767.

So let's multiply using 32767 x 32767 = 1073676289. Now right shift by 16 = 1073676289/(2^16) = 16383, when the desired result is ACTUALLY 1073676289/(2^15) = 32766.

Having to MANUALLY shift, say in the case of SMULL gives you two extract instructions:

lsrs r0, r0, #15
orr r0, r0, r1, lsl #17

Which is undesirable.

Will you provide, at some point in the future, DSP and intrinsics functions that don't return results that are half the magnitude they should be, so that we don't lose one bit of precision each time we use them?

Ideally, for true fixed-point support, you could make a "combined multiply shift" instruction, that takes the desired shift as an argument, thereby allowing generic position fixed-point calculations to be peformed.

With the current formats, it's quite limiting, and I was really looking forward to Helium, but knowing this, much less so now. It's interesting to note that the RISC-V "P" extension is proposed to support the 15 shift: https://github.com/riscv/riscv-p-spec/blob/master/P-ext-proposal.pdf which to me is much more sensible.

Very curious as to your thoughts on this.

With a user-defined shift value, you could, in one instruction set, support Q1.15, Q1.31, or Q7.24 Q15.16 etc. and leave that in the hands of the users to define their own fixed-point renormalization.