Calculating a Decaying Exponential Curve Recursively by using Fixed Point Multiplication

I’ve been working on implementing Envelope Generator into microprocessors. Output of an Envelope Generator consists of rising and falling exponential curve. I’m currently doing table lookup to generate those curves.

This is a quick and straightforward approach, and you don’t have to be worried much about data resolution. But the program is a little complicated, and memory consuming. I don’t really like this approach. I don’t really like the approach, so I started to think about alternative approaches.

Mathematically, a exponentially decaying curve can be calculated recursively by following formula:

V(i+1) = V(i) * k

where 0 <= k < 1

This is very simple. You can program this such as:

double value = INITIAL_VALUE;
double decay_factor = 0.99309249543703590153321021688807;
while (true) {
  value *= decay_factor;

but implementing the algorithm into a microprocessor is not straightforward like this.

I’m planning to use an AVR (e.g. atmega168), it has limited number of registers, and floating point multiplication is generally slow in AVR. So I would like to avoid floating point data types.

So shall I go for simple integer calculation? It’s actually not feasible. For example, if I update the value in every 10ms (100 updates per second) and would like to make a decaying curve that reduces its value to 50% after 1 second, the decay factor k would be

k = 0.5 ^ (1 / 100) = 0.99309249543703590153321021688807

It’s less than 1 so cannot be expressed by an integer.

There’s a technique called fixed point multiplication. This fundamentally uses integers but can handle fractions. This is my first time to try this technique, so learned how you do it. I found following link a good instruction:

Tried to write it in C.

#include <stdio.h>
#include <stdint.h>

int main(int argc, char *argv[]) {
  // fixed point integer 10bit.6bit
  uint16_t value = (1023) << 6;
  // fixed point integer .16bit
  uint16_t decay_factor = 0.99309249543703590153321021688807 * 65536;
  for (uint16_t i = 0; i < 1000; ++i) {
    // multiplication
    uint32_t temp = value * decay_factor;
    value = temp >> 16;
    // retrieve the integer part
    uint16_t output = value >> 6;
    printf("%d %d\n", i, output, 10);
  return 0;

Ran it on my PC (not on the microprocessor yet). Plotted the result to a graph:

OK it looks good so far.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.