how to estimate RAM usage of math functions

I had made the following code to solve a cubic equation which uses couple ‘math’ functions:

#define pi 3.141593

struct real_solutions_t {
  uint8_t number_of_solutions;
  float solutions[3];
};

uint8_t solve_cubic(float a, float b, float c, float d, struct real_solutions_t *soln)
{
  float _a, _b, _c;
  float half = 1.0 / 2;
  float third = 1.0 / 3;

  if (a != 0) {
    _a = b / a;
    _b = c / a;
    _c = d / a;
  }
  else {
    return 0;  //not a cubic funtions
  }

  float p = _b - ((_a * _a) / 3);
  float q = ((2 * _a * _a * _a) / 27) - ((_a * _b) / 3) + _c;
  float disciminant = ((q * q) / 4) + ((p * p * p) / 27);

  if (disciminant > 0.000001) { //inplace of 'disciminant > 0' to due to floating point computation accuracy.
								//else may result in incorrect output if 'disciminant = 0'
    soln->number_of_solutions = 1; //one real solution
    soln->solutions[0] = pow((pow(disciminant, half) - (q / 2)), third) + pow((-1 * (pow(disciminant, half)) - (q / 2)), third) - (_a / 3);
  }
  else if (disciminant < 0 ) {
    p = pow((-1 * p), half);
    float f = (2 / pow(3, half)) * p;
    float g = (3 / 2) * pow(3, half);
    soln->number_of_solutions = 3; //3 real solutions
    soln->solutions[0] = f * sin(third * asin((g * q) / (p * p * p))) - (_a / 3);
    soln->solutions[1] = f * sin(third * asin(((g * q) / (p * p * p))) + (pi / 3)) - (_a / 3);
    soln->solutions[2] = f * cos(third * asin(((g * q) / (p * p * p))) + (pi / 6)) - (_a / 3);
  }
  else { //assumes 'disciminant = 0'
    soln->number_of_solutions = 2; //2 real solutions (x2 = x3, repeated root)
    if (q < 0) {
      q *= -1;
      soln->solutions[0] = (2 * pow((q / 2), third)) - (_a / 3);
      soln->solutions[1] = (-1 * pow((q / 2), third)) - (_a / 3);
    }
    else {
      soln->solutions[0] = (-2 * pow((q / 2), third)) - (_a / 3);
      soln->solutions[1] = pow((q / 2), third) - (_a / 3);
    }
  }

  return 1;

}

void setup() {
  struct real_solutions_t real_solutions;

  Serial.begin(115200);

  uint8_t i = solve_cubic(1, -4, 5, -2, &real_solutions);

  if (i) {
    Serial.print("Number of Real Solutions: ");
    Serial.println(real_solutions.number_of_solutions);

    if (real_solutions.number_of_solutions == 1) {
      Serial.print("x = ");
      Serial.println(real_solutions.solutions[0]);
    }
    else if (real_solutions.number_of_solutions == 2) {
      Serial.print("x1 = ");
      Serial.println(real_solutions.solutions[0]);
      Serial.print("x2 = x3 = ");
      Serial.println(real_solutions.solutions[1]);
    }
    else {
      Serial.print("x1 = ");
      Serial.println(real_solutions.solutions[0]);
      Serial.print("x2 = ");
      Serial.println(real_solutions.solutions[1]);
      Serial.print("x3 = ");
      Serial.println(real_solutions.solutions[2]);
    }
  }
  else {
    Serial.println(F("NOT A CUBIC EQUATION!"));
  }
}

void loop() {
  //nothing here
}

Sketch uses 3140 bytes (9%) of program storage space. Maximum is 32256 bytes.
Global variables use 244 bytes (11%) of dynamic memory, leaving 1804 bytes for local variables. Maximum is 2048 bytes.

The above code compiled and uploaded OK for my UNO but could not get any sensible output on Serial monitor.

I then used change the the code to use the F() functions wherever possible:

#define pi 3.141593

struct real_solutions_t {
  uint8_t number_of_solutions;
  float solutions[3];
};

uint8_t solve_cubic(float a, float b, float c, float d, struct real_solutions_t *soln)
{
  float _a, _b, _c;
  float half = 1.0 / 2;
  float third = 1.0 / 3;

  if (a != 0) {
    _a = b / a;
    _b = c / a;
    _c = d / a;
  }
  else {
    return 0;  //not a cubic funtions
  }

  float p = _b - ((_a * _a) / 3);
  float q = ((2 * _a * _a * _a) / 27) - ((_a * _b) / 3) + _c;
  float disciminant = ((q * q) / 4) + ((p * p * p) / 27);

  if (disciminant > 0.000001) { //inplace of 'disciminant > 0' to due to floating point computation accuracy.
								//else may result in incorrect output if 'disciminant = 0'
    soln->number_of_solutions = 1; //one real solution
    soln->solutions[0] = pow((pow(disciminant, half) - (q / 2)), third) + pow((-1 * (pow(disciminant, half)) - (q / 2)), third) - (_a / 3);
  }
  else if (disciminant < 0 ) {
    p = pow((-1 * p), half);
    float f = (2 / pow(3, half)) * p;
    float g = (3 / 2) * pow(3, half);
    soln->number_of_solutions = 3; //3 real solutions
    soln->solutions[0] = f * sin(third * asin((g * q) / (p * p * p))) - (_a / 3);
    soln->solutions[1] = f * sin(third * asin(((g * q) / (p * p * p))) + (pi / 3)) - (_a / 3);
    soln->solutions[2] = f * cos(third * asin(((g * q) / (p * p * p))) + (pi / 6)) - (_a / 3);
  }
  else { //assumes 'disciminant = 0'
    soln->number_of_solutions = 2; //2 real solutions (x2 = x3, repeated root)
    if (q < 0) {
      q *= -1;
      soln->solutions[0] = (2 * pow((q / 2), third)) - (_a / 3);
      soln->solutions[1] = (-1 * pow((q / 2), third)) - (_a / 3);
    }
    else {
      soln->solutions[0] = (-2 * pow((q / 2), third)) - (_a / 3);
      soln->solutions[1] = pow((q / 2), third) - (_a / 3);
    }
  }

  return 1;

}

void setup() {
  struct real_solutions_t real_solutions;

  Serial.begin(115200);

  uint8_t i = solve_cubic(1, -4, 5, -2, &real_solutions);

  if (i) {
    Serial.print(F("Number of Real Solutions: "));
    Serial.println(real_solutions.number_of_solutions);

    if (real_solutions.number_of_solutions == 1) {
      Serial.print(F("x = "));
      Serial.println(real_solutions.solutions[0]);
    }
    else if (real_solutions.number_of_solutions == 2) {
      Serial.print(F("x1 = "));
      Serial.println(real_solutions.solutions[0]);
      Serial.print(F("x2 = x3 = "));
      Serial.println(real_solutions.solutions[1]);
    }
    else {
      Serial.print(F("x1 = "));
      Serial.println(real_solutions.solutions[0]);
      Serial.print(F("x2 = "));
      Serial.println(real_solutions.solutions[1]);
      Serial.print(F("x3 = "));
      Serial.println(real_solutions.solutions[2]);
    }
  }
  else {
    Serial.println(F("NOT A CUBIC EQUATION!"));
  }
}

void loop() {
  //nothing here
}

Sketch uses 3206 bytes (9%) of program storage space. Maximum is 32256 bytes.
Global variables use 200 bytes (9%) of dynamic memory, leaving 1848 bytes for local variables. Maximum is 2048 bytes.

This too compiled OK but now I was getting the correct output on Serial monitor!

So I made me think that is “It must have been RAM related then”… Was my assumption right?

If so, how to determine is there is sufficient ‘free’ RAM left on a given arduino board to have confidence that a code using ‘math’ functions with execute correctly?

Any insights?

If that’s the whole code then something else must have been going on, the maths functions you use do not add so much RAM pressure at all. Are you sure you had the serial console at the right speed in the first test?

I can’t test your first code for the moment but will give it à go later

J-M-L:
Are you sure you had the serial console at the right speed in the first test?

Yes, I am sure it was set correctly.... did not touch that setting AT ALL for that matter for either codes! :slight_smile:

I've also tried it with a second UNO; same result...

and if that matters, I did not run the first code on the board only once; I reset the board several times and still would not get a correct output.

with the second code, got the right result the every time.

I’m on my mobile so hard to compare but you say the only difference between the 2 codes is the F() macro?

Edit
Actually I managed to check with an online diff checker and indeed it’s the F thing only.

Weird

I’ll test later (you might have a memory bug with your pointers Or something - will read on a larger screen later as nothing obvious - and in one case you overflow and the change in memory layout make it less visible)

I get the same results from both versions...

Number of Real Solutions: 2
x1 = 2.00
x2 = x3 = 1.00

westfw:
I get the same results from both versions...

Was that on a UNO?

Yes, it was on an Uno…

westfw:
Yes, it was on an Uno...

hmmm.... ok

I just realised another thing, I have my compiler optimisation set for 'speed' (did that a long time ago and never reverted to default setting); could that be why I'm seeing this issue?

What development environment and board setting are you using?
The standard IDE doesn’t have an optimization setting…

This will not work, since (3/2) = 1, but the example you posted doesn't test that branch.

    float g = (3 / 2) * pow(3, half);

Fix it by forcing floats, and get rid of the inaccurate function pow() where ever possible.

float g = 1.5*sqrt(3.0);

or better yet:

float g=2.5980762;

westfw:
What development environment and board setting are you using?
The standard IDE doesn't have an optimization setting...

using arduino IDE 1.8.5

I changed the optimisation '-Os' to '-O3' setting in 'platform.txt' for speed optimisation. As mentioned I had made this change a while back and never touched the file again...