OVF (stack overflow) when calculating a 5th order polynomial on an UNO

I think that I have reached the limit of what an AVR Uno can do.

When using a normal linear regression to calculate a variable - no problems.

But it seems that the little fellow can't handle more complex maths, such as a 5th order polynomial regression - it just says "ovf"

From what I can see its a stack overflow.

I need to use this 5th order polynomial equation - can anyone suggest an alternative processor/method. Thanks.

Use a processor with more SRAM?
'1284P has 16K

Posting your code is always a good start, if it is crashing.

How to use this forum

Anyway, it sounds like a number is too large, that is all:

Print.cpp:

size_t Print::printFloat(double number, uint8_t digits) 
{ 
  size_t n = 0;
  
  if (isnan(number)) return print("nan");
  if (isinf(number)) return print("inf");
  if (number > 4294967040.0) return print ("ovf");  // constant determined empirically
  if (number <-4294967040.0) return print ("ovf");  // constant determined empirically
...

Thanks for the fast feedback.

I am unsure how much SRAM is needed in the calculation but I have an ethermega (256) which has 8KB SRAM while the UNO has 2KB.

So will try this first.

This is the equation causing the problem. It’s a 5th order polynomial regression.

y = 1.354026784•10-8 * x5 - 2.559722383•10-5* x4 + 1.92917182•10-2 *x3 - 7.24448384 x2 + 1356.112407 x - 101259.7822

Of course I had to use the pow(x,y) function to crunch the data instead of using the exponents.

X = the counts variable. Defining this as type int or float made no difference.

So this converted to: lcd.print(pow(1.354026784,-8)* pow(x,5) - pow(2.559722383,-5)pow(x,4) + pow(1.92917182,-2) pow(x,3) - 7.24448384 * pow(x,2) + 1356.112407* x - 101259.7822);

Using #include <math.h> made no difference.

I am using the newer 1.0.3 IDE which tells me (on upload)that 146bytes of 2048, of SRam was used.

Thanks.

It's nothing to do with RAM. I am predicting that you are trying to print a number outside the range -4294967040 to 4294967040.

It looks like the print function is not designed to show exponents, and thus it fails with the "OVF" message.

You'll need to find a float-printing function that handles exponents.

See: http://arduino.cc/forum/index.php/topic,46931.0.html

I’ll just guess what “x” is, will I?

Test:

// From: http://arduino.cc/forum/index.php/topic,46931.0.html
// Author: Rob Tillaart

char * float2s(float f)
{
  return float2s(f, 2);
}

char * float2s(float f, unsigned int digits)
{
  int index = 0;
  static char s[16];                    // buffer to build string representation
  // handle sign
  if (f < 0.0)
  {
    s[index++] = '-';
    f = -f;
  } 
  // handle infinite values
  if (isinf(f))
  {
    strcpy(&s[index], "INF");
    return s;
  }
  // handle Not a Number
  if (isnan(f)) 
  {
    strcpy(&s[index], "NaN");
    return s;
  }

  // max digits
  if (digits > 6) digits = 6;
  long multiplier = pow(10, digits);     // fix int => long

  int exponent = int(log10(f));
  float g = f / pow(10, exponent);
  if ((g < 1.0) && (g != 0.0))      
  {
    g *= 10;
    exponent--;
  }
 
  long whole = long(g);                     // single digit
  long part = long((g-whole)*multiplier);   // # digits
  char format[16];
  sprintf(format, "%%ld.%%0%dld E%%+d", digits);
  sprintf(&s[index], format, whole, part, exponent);
  
  return s;
}

// -------- test -------

float f;

void setup()
{
  Serial.begin(115200);
  Serial.println("Start");  
  int x = 10000;
  f = pow(1.354026784,-8)* pow(x,5) - 
      pow(2.559722383,-5)*pow(x,4) + 
      pow(1.92917182,-2)* pow(x,3) - 
      7.24448384 * pow(x,2) + 
      1356.112407* x - 
      101259.7822;
  Serial.println (f);
  Serial.println(float2s(f, 5));
}

void loop() { }

Output:

Start
ovf
8.85069 E+18

The “normal” print gave “ovf”, the routine by Rob printed a number.

X is the Counts from the AVR and will be somewhere between 285 and 600 ie within the 5Volt range. 285 = no signal and increasing up to around 600 counts.

The calculated output number needs to be in the format xxx.xx

The print statement has no trouble doing this with a more simple linear regression calculation.

I found some possible useful information to try using Flash memory instead of RAM here, http://playground.arduino.cc/Main/Printf

Will also try Robs program as it obviously works.

Platypus: The print statement has no trouble doing this with a more simple linear regression calculation.

It's nothing to do with how simple the linear regression calculation is. It is what the size of the resulting number is.

I managed to get a number displayed on the lcd however with a modification - i had to use lcd.print(float2s,(the equation);//,5 was dropped as it would not compile without compilation error - "call of overloaded 'print(char*,int)' is ambiguous"

At 287 counts the output was 1.65 E+11 where it should be around 10 to 20.

I can't find the cause of this in Robs program - unable to work out what he did.

lcd.print(float2s,(the equation);

What is that comma doing there?

Float2s(f,5) where the equation is f

Thanks for your help but the additional sketch does not seem to give a solution and also seems to be overcomplicated.

Platypus: I managed to get a number displayed on the lcd however with a modification - i had to use lcd.print(float2s,(the equation);//,5 was dropped as it would not compile without compilation error - "call of overloaded 'print(char*,int)' is ambiguous"

At 287 counts the output was 1.65 E+11 where it should be around 10 to 20.

I can't find the cause of this in Robs program - unable to work out what he did.

This is just mumbo-jumbo without posting your code.

Post code that demonstrates this point, don't just post tiny snippets and fragments of error messages.

Solution - a matter of using the pow function correctly.
Here is the test program to calculate a 5th order polynomial regression equation.
Thanks for your input.

void setup() {
Serial.begin(9600);
int val=420;

Serial.println(1.354026784*pow(10,-8)*pow(val,5) //pow(10,-8) raises 1.354026784 to the 10-8

  • 2.559722383*pow(10,-5)*pow(val,4)
  • 0.0192917182*pow(val,3)
  • 7.24448384*pow(val,2)
  • 1356.112407*val
  • 101259.7822,4);//the ‘,4’ at the end gives an output to 4 decimal places
    }

void loop() {}


Serial display
116.9063 - this is correct

Well, if you are doing polynomial calculations on the Uno, note that the all of the AVR processors map 'double' into 'float', which means reduced precision and exponent range. Arm based processors like Due or a Teensy 3.0 use the same format for double as x86's.

Another note, if you are doing lots of floating point calculations, both the AVR and Due/Teensy 3.0 based processors do not have floating point hardware, and emulate the floating point. If you are doing lots of floating point calculations, perhaps you would do better to get something like a Raspberry Pi/Beagle Board/maybe pcDunio which has hardware floating point to do the calculations. Since these machines run Linux varients, they also have much more memory to work with. But the programming of these is different from the Arduino, and you have less pins for controlling hardware. If you are controlling devices, perhaps a combination, where you use the R-PI to do the brain work, and a controller to talk to hardware.

Interesting to see some of my code back ;)

Some Arduino floating points points: - the Arduino has only 7 digits of precision, the compiler will take care of rounding - the compiler accepts E notation in float constants, which removes unneeded pow(10, n) from your code - a fifth order polynome with integer powers can speed up by a big factor by decomposing it using only 5 multiplies and 5 additions.

void setup()
{
  Serial.begin(9600);
  Serial.println("start...");
  
  long val = 420;

  f = 1.354026784*pow(10,-8)*pow(val,5) //pow(10,-8) raises 1.354026784 to the 10-8
               - 2.559722383*pow(10,-5)*pow(val,4) 
               + 0.0192917182*pow(val,3) 
               - 7.24448384*pow(val,2) 
               + 1356.112407*val 
               - 101259.7822;
               
  Serial.println(f,5);
  Serial.println(float2s(f, 5));

  // removing pow() completely
  f = 1.354026784E-8* val*val*val*val*val - 2.559722383E-5* val*val*val*val + 1.92917182E-2 * val*val*val - 7.24448384 * val*val + 1356.112407*val - 101259.7822;
  Serial.println(f,5);
  Serial.println(float2s(f,5));
  
  // minimize math 
  f = (((( 1.354026784E-8 * val - 2.559722383E-5 ) * val + 1.92917182E-2 ) * val - 7.24448384 ) * val + 1356.112407) * val - 101259.7822;
  Serial.println(f,5);
  Serial.println(float2s(f, 5));
}

void loop(){}

116.9063

output start... 116.90625 1.16906 E+2

116.84375 1.16843 E+2

116.79688 1.16796 E+2

Note that method 2 and 3 which uses less operations already differ in the 4th digit, so calculating this with more than 5 digits is speculative at best.

This is caused by the fact that all the partial sums ( a.pow(b,n) ) are in the same order of magnitude 10^5..10^6 and their sum is 2 to 3 orders of magnitude lower. (you're losing significant bits here)

BTW according to Excel the "right" value == 116.935898400974 so simplifying the math does make it worse this time.

Excel snippet (,'s are due to localization)

coefficient             420^n                   partial result
1,354026784000000E-08   1,306912320000000E+13   1,769594285619580E+05
-2,559722383000000E-05  3,111696000000000E+10   -7,965077900291570E+05
1,929171820000000E-02   7,408800000000000E+07   1,429284818001600E+06
-7,244483840000000E+00  1,764000000000000E+05   -1,277926949376000E+06
1,356112407000000E+03   4,200000000000000E+02   5,695672109400000E+05
-1,012597822000000E+05  1,000000000000000E+00   -1,012597822000000E+05

                                   sum = 1,169358984009740E+02