title: signed 16-bit SSE average
body: <p><code>_mm_avg_epu16</code> provides an average of two unsigned 16-bit integers via <code>PAVGW</code>.  Is converting to <code>float</code> and dividing by <code>2.</code> the only appropriate (optimal) way to use SSE to get <strike>a average of two signed 16-bit integers</strike> a "signed average that rounds up followed by inverting the top bit" (@Mysticial), or is there another way?</p>
  
  <hr>
  
  <p>EDIT: Here's the code I'd like to optimize, so far all my attempts using SSE have been close but not exactly matching, usually surrounding problems w/ saturation/overflow-wrapping:</p>
  
  <pre><code>int16_t *a;
  int16_t *b;
  uint16_t *out;
  
  out[i] = int((a[i] + b[i]) / 2.0f + 32768.5f)
  </code></pre>
  
  <p>Attempt #1:</p>
  
  <pre><code>const __m128i outputVal = _mm_add_epi16(_mm_avg_epu16(a, b),  _mm_set1_epi16(32768));
  </code></pre>
  
  <p>Attempt #2:</p>
  
  <pre><code>const __m128i sum = _mm_add_epi16(a, b);
  const __m128i outputVal = _mm_add_epi16(_mm_srai_epi16(sum, 1), _mm_set1_epi16(32768));
  </code></pre>
  
  <p>Attempt #3:</p>
  
  <pre><code>const __m128 elt_offset = _mm_set1_ps(32768.5f);
  
  const __m128 avg_divisor = _mm_set1_ps(2.f);
  
  const __m128i eltSum = _mm_add_epi16(edgeRowElts, edgeInnerRowElts); /* eltSum = int((inputData[i] + inputData[i + (direction*x)]) */
  const __m64 eltSumLow  = _mm_movepi64_pi64(eltSum); /* eltSumLow = (__m64) (0x0ffffffff &amp; eltSum) */
  const __m64 eltSumHigh = _mm_movepi64_pi64(_mm_srli_si128(eltSum, 8)); /* eltSumHigh = (__m64) (0x0ffffffff &amp; (eltSum &gt;&gt; 64)) */
  
  /* Lower */
  __m128 eltSumF = _mm_cvtpi16_ps(eltSumLow); /* eltSumF = (float) eltSum; */
  
  __m128 eltAvg  = _mm_div_ps(eltSumF, avg_divisor); /* eltAvg = eltSum / 2.0f */
  __m128 eltAvgOffset = _mm_add_ps(eltAvg,  elt_offset); /* eltAvgOffset = eltAvg + 32768.5f */
  const __m64 outputValLow  = _mm_cvtps_pi16(eltAvgOffset); /* outputVal = (short) eltAvgOffset */
  
  /* Upper */
  eltSumF = _mm_cvtpi16_ps(eltSumHigh); /* eltSumF = (float) eltSum; */
  
  eltAvg  = _mm_div_ps(eltSumF, avg_divisor); /* eltAvg = eltSum / 2.0f */
  eltAvgOffset = _mm_add_ps(eltAvg,  elt_offset); /* eltAvgOffset = eltAvg + 32768.5f */
  const __m64 outputValHigh = _mm_cvtps_pi16(eltAvgOffset); /* outputVal = (short) eltAvgOffset */
  
  __m128i outputVal = _mm_slli_si128(_mm_movpi64_epi64(outputValHigh), 8); /* outputVal = (outputValHigh &lt;&lt; 64); */
  outputVal = _mm_or_si128(outputVal, _mm_movpi64_epi64(outputValLow)); /* outputVal = outputVal | (outputValLow); */
  </code></pre>