0

I’m playing around with Direct digital synthesis wavetables on an Atmega328.

Given an 8-bit fractional value F, and two 8-bit values A, B, what would be the most efficient way to calculate the linear interpolated value between A and B? I can think of two methods:

Alternative 1: Interpolated value = ((A << 8) + (B - A) * F) >> 8 (Will only work when B >= A, but I could wrap it in an if/else and swap A and B if A > B)

Alternative 2: Interpolated value = A + (B - A) * (F / 256)

Does it even matter which one I choose or would the compiler optimize them identically anyway?

Is there an even better method that involves no multiplication or division?

EDIT: Changed denominator in alternative 2 from 255 to 256

  • The alternatives are not equivalent. Alternative 1 includes an extra A term compared to Alternative 2. – Johan Jan 03 '18 at 17:04
  • Well yes, it certainly does :) thank you, removed it now. Now they should yield the same result. – Love Aurell Jan 03 '18 at 17:26
  • Why would division by 256 with `(B - A) * F) >> 8` expected to be the same as division by 255 in `(B - A) * (F / 255)`? – chux - Reinstate Monica Jan 03 '18 at 17:55
  • @chux That is probably a typo as well. – Johan Jan 03 '18 at 17:56
  • Even with `/256`, assume `A==0` Certainly `(B * F) >> 8` gives different results than `B * (F / 256)`. Since the 2 ways are not functionally equivalent. OP should use the one with the desired functionality. I'd consider `A + ((B - A) * F) >> 8)` if `B >= A` – chux - Reinstate Monica Jan 03 '18 at 18:05
  • Yes `255` should be `256`. And I see now that it wont work if `B < A`. I could of course check that before the calculation and switch them. But I don't get why `((A << 8) + (B - A) * F) >> 8` won't work when `A < B` and `A == 0``? – Love Aurell Jan 03 '18 at 19:24
  • 1
    (F / 256), where F is 8 bit integer (I. e. always less than 256) always gives 0 – AterLux Jan 07 '18 at 02:32
  • try this: A + (((int16_t) ((int16_t)B - A) * F + 128) >> 8) – AterLux Jan 07 '18 at 02:39
  • @AterLux Ah yes, it will get truncated. Didn’t think of that. Then I guess the question is, how costly 16-bit multiplication is compared to floating point division. – Love Aurell Jan 09 '18 at 05:54
  • As far as I can see 8-bit*8-bit to 16-bit is one instruction on the m328 while anything including floating point arithmetic requires a significant number of calls to expensive library functions. – Johan Jan 09 '18 at 13:22

1 Answers1

0

assuming that

  • F is in 1/256 per count (and cannot completely express 1)
  • A, B and F are uint8_t

the linear interpolation could be

uint8_t I = A + (uint8_t)(((int16_t)(B-A) * F) >> 8)

If you want that 255 expresses 1.0 then you have to divide by 255 instead of shifting but i guess i would handle the one case differently. eg by using 16bit for F and check for the highbyte before using the above formular with the lower byte if with its less than one.

The above line is nearly exactly the same as your Alternative one but since A and B are 8bit then the interpolated result cannot exceed the 8bit range, so the addition can be done using uint8_t.

Your alternative 2 always gets A as result since the second multiplicand (F/256) is always zero.

vlad_tepesch
  • 6,681
  • 1
  • 38
  • 80