Go Down

Topic: Manipulating the behavior of an exponential function (Math gurus needed!) (Read 5018 times) previous topic - next topic

fuzzball27

Hello,
I'm working with an exponential function, and I need a bit of help with manipulating its behavior. Basically, I have two points that I would like to maintain whilst "dragging" the curve of the function outwards in order to exaggerate the exponential behavior of the function. Here is the application: I have an input value (x) which I would like to scale from 1080 and 1750 to an output value (y) within 1500 and 2000. I do not want to use a linear equation, because I would like to create the effect where lower x values output y values incrementing at a much slower rate than output y values increment from higher x input values.

I am currently using this function to achieve my goal: y = (1500 / ((4/3)^(1/670))^1080) • ((4/3)^(1/670))

Unfortunately, the effect of the exponential behavior is very minor and unnoticeable in the application I am using it for in my program. How can I exaggerate the exponential behavior of the function whilst maintaining the two points (1080, 1500) and (1750, 2000)?
fuzzball27 >>-->

Magician

So, other thread didn't help much? http://arduino.cc/forum/index.php/topic,115296.0/topicseen.html
I'd suggest, before you try to program equation in C, you need clear representation of this equation in math with all coefficients defined, only after that you would be able to make a decision what "library" function is the best for task ( pow, exp, base 2, base 10, float vs integer etc).
The simple solution is Excell, or LibreOffice, where you play around with math, and see how it affects a  chart.  Only when you like what you see, you ready to transfer it in C.

fuzzball27


So, other thread didn't help much?


You must be confused.
The other thread answered all of my questions. I got that function working just fine.


The simple solution is Excell, or LibreOffice, where you play around with math, and see how it affects a  chart. Only when you like what you see, you ready to transfer it in C.


I've been doing that, and I disagree with you. I have been doing research online and playing with graphs, and it would seem in this case the simple solution is to ask a forum with over 120,000 people who actually have an idea of what I'm talking about.
fuzzball27 >>-->

JavaMan

See if this has a noticeable effect:

y = 1500 + .004455(x-1080)*(x-1080)

robtillaart

Quote
I am currently using this function to achieve my goal: y = (1500 / ((4/3)^(1/670))^1080) • ((4/3)^(1/670))

Unfortunately, the effect of the exponential behavior is very minor and unnoticeable in the application I am using it for in my program. How can I exaggerate the exponential behavior of the function whilst maintaining the two points (1080, 1500) and (1750, 2000)?


Where is the x in the function? :)

How would you implement the "basic" function if it was between the points (0,0) and (1,1) ? If you can define that the rest is scaling.

Easiest option is to use - http://arduino.cc/playground/Main/MultiMap - which can mimic all non linear functions

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

fuzzball27


See if this has a noticeable effect:

y = 1500 + .004455(x-1080)*(x-1080)


Thank you, I'll give it a shot. How did you calculate it?



Where is the x in the function? :)


I apologize:  y = (1500 / ((4/3)^(1/670))^1080) • ((4/3)^(1/670))^x



How would you implement the "basic" function if it was between the points (0,0) and (1,1) ? If you can define that the rest is scaling.


Thank you! I believe you just answered my question. With a little trig and elbow grease I think that will work. :)


Easiest option is to use - http://arduino.cc/playground/Main/MultiMap - which can mimic all non linear functions


Yes, but he uses map() which uses long value types and I need a high-speed function, so I'm trying to stay away from 32 and 64 bit numbers. (although I have to use 32 bit because Arduino doesn't support 16 bit float values?)
fuzzball27 >>-->

MichaelMeissner


Yes, but he uses map() which uses long value types and I need a high-speed function, so I'm trying to stay away from 32 and 64 bit numbers. (although I have to use 32 bit because Arduino doesn't support 16 bit float values?)

Just remember, the ATmega chips are 8 bit chips, with no floating point.  This means every floating point calculation probably involves hundreds to thousands of operations (for add/subtract), and thousands to millions (for multiplies/division).  While 32-bit integer arithmetic also involves multiple instructions, it is likely 1-2 orders of magnitude faster than using floating point.  If you can recode your algorithm to not use floating point, it probably will execute faster.

If your code is floating point intensive, and you have time constraints on its execution, you probably should be thinking about using something else, like an ARM with hardware floating point.

fuzzball27


See if this has a noticeable effect:

y = 1500 + .004455(x-1080)*(x-1080)


Wow. Genius. I should have thought of that earlier! :P
fuzzball27 >>-->

fuzzball27

For the curious, here is the equation I am now using: y=((1/897.8 )*(x-1080)^2)+1500
fuzzball27 >>-->

JavaMan

Quote
Quote from: JavaMan on July 22, 2012, 09:07:15 PM
See if this has a noticeable effect:

y = 1500 + .004455(x-1080)*(x-1080)

Thank you, I'll give it a shot. How did you calculate it?


The only number I calculated was .004455, and that was to make y = 2000 when x = 1750.  But I forgot about the 1500 and gave you the wrong number!  Should have been .001114.  Looks like you figured it out anyway.  Good job!

robtillaart

Quote
For the curious, here is the equation I am now using: y=((1/897.8 )*(x-1080)^2)+1500


if x and y are of the type integer you can speed up this formula by removing the FP math

long t = x-1080;
t = t * t * 10;
y = t / 8978 + 1500;
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

fuzzball27

I'm using unsigned 16 bit integers (uint16_t) so I'd assume that will work? I'll give it a shot. @MichaelMeissner also suggested using this method of calculation (not in this thread).
fuzzball27 >>-->

robtillaart

#12
Jul 29, 2012, 02:07 pm Last Edit: Jul 29, 2012, 02:10 pm by robtillaart Reason: 1
Quote
I'm using unsigned 16 bit integers (uint16_t) so I'd assume that will work?

No it will not.

your input values are in the range "1080 and 1750" squaring it gives values between 1 and 4 million, will not fit in an 16 bit.
OK the subtraction of 1080 makes it max 700^2 == 490.000 which still will not fit in a 16 bit int by a factor 7.

However if you persist to do only 16 bit math and you accept a (~1%) error you could apply the division in steps to keep the result under max unint16. The prices is less accuracy and some "staircasing" in the graph due to less distinct values. I used a spreadsheet to model the results below.


(original code, 2 long multiplies and one long division)
long t = x-1080;
t = t * t * 10;
y = t / 8978 + 1500;   (will have 445 distinct values)


Note: 8978 = 2x67x67
t = x-1080;    // t = 0..700
t = t/67;       // t = 0..11  (oops only 12 distinct values BAD)
t = t * t * 5;  
y = t + 1500;   // still only 12 distinct values  TOO ROUGH :(


8978 is almost 8980 = 2.2.5.449

t = x-1080;    // t = 0..700
t1 = t/2;       // t1 = 0..350 (350 distinct values OK!)
t2 = t/5;       // t2 = 0..140 (140 disctinct values OK!)
note that using two distinct dividers give more distinct values in the end.

t = t1 * t2;    //  t = 0..44890
t = t / 89;     // (remaining factors result in => 2*449/10 = 89   (328 distinct values)
t = t + 1500;
if (t > 2000) t = 2000;   // max error is 4


Other
8978 is ~8982 = 2.3.3.499  
===> using 2 and 3 will result in an overflow  (700^2 /6 >> 65535)
===> using 3 and 6 will give less distinct values than 2 and 5
===> using 1 and 9 is interesting as you need one division less! and it will map 1750 on 2000

t = x-1080;    // t = 0..700
t1 = t/9;       // t1 = 0..77
t = t1 * t;      //  t = 0..49580
t = t / 99;     // 280 distinct values
t = t + 1500;

So using these values produces slightly less distinct values but its codesize is also less (faster) AND still using only 16 bit math!.


another one to consider
8978 is ~8976 = 2.2.2.2.3.11.17
==> using 2 and 4 is interesting as two divisions would result in a shift (fast)
t = x-1080;    
t1 = t/2;      
t2 = t/4;
t = t1 * t2;      //  t = 0..55945
t = t / 111;     // => 262 distinct values (max error 6)
t = t + 1500;
if (t > 2000) t = 2000;

Epilog:
If small errors are acceptable one can improve the speed and the footprint of the algorithm quite a lot.

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

westfw

Making me nervous...
Code: [Select]
for the curious, here is the equation I am now using: y=((1/897.8 )*(x-1080)^2)+1500
You know that "^" is NOT an exponentiation operation in actual c++, unless it got overloaded for floating point...

fuzzball27

Haha yes that was the "plain english" version. "^" is an operator isn't it?
fuzzball27 >>-->

Go Up