Help! Weird things happening with pow function

OK I’m new to the Arduino platform, but I’ve grasped the concepts and love it so far. I wrote a sketch to fade an LED in and out with a variable rate. I chose to set the rate over serial communication. My problem is with the pow function I use to convert from ASCII to integer (“1” to (int) 1, instead of “1” to ASCII (int) 49) When I convert anything over 99, the total looks like it’s 1 less than it should (if 100 is entered, converted value is 99). I looked around the forum for similar issues, but could not find any. I can fix it using a condition to add 1 to the total value if the length of the conversion string is 3 or greater (int 100+), but I would like some rational explanation about why this is happening.

I added some debugging and look at what I have when I enter 100:

buffer [0] = 49
buffer [1] = 48
buffer [2] = 48
New Line Detected
value: 1
Rate Equation: value * 10^(buffer_length - 1 - j) = 1 * 10^(3 - 1 - 0) = 100.00
value: 0
Rate Equation: value * 10^(buffer_length - 1 - j) = 0 * 10^(3 - 1 - 1) = 0.00
value: 0
Rate Equation: value * 10^(buffer_length - 1 - j) = 0 * 10^(3 - 1 - 2) = 0.00
Total: 99

You can see that the total is 99, which is weird because adding up the rate equation values gives 100…

When I enter 99 it operates normal:

buffer [0] = 57
buffer [1] = 57
New Line Detected
value: 9
Rate Equation: value * 10^(buffer_length - 1 - j) = 9 * 10^(2 - 1 - 0) = 90.00
value: 9
Rate Equation: value * 10^(buffer_length - 1 - j) = 9 * 10^(2 - 1 - 1) = 9.00
Total: 99

Here’s my sketch:

#define PINOUT 9

void setup() {
  pinMode(PINOUT, OUTPUT);
  Serial.begin(115200);
}

int rate = 200;
int buffer[256];
int buffer_length = 0;

void loop() {
  int i = 0;
  if (Serial.available() > 0) {
    int tmp = Serial.read();
    if (tmp == 13) {
      Serial.println("New Line Detected");
      rate = 0;
      for (int j = 0; j < buffer_length; j++) {
        int value = buffer[j] - 48;
        Serial.print("value: ");
        Serial.println(value);
        Serial.print("Rate Equation: value * 10^(buffer_length - 1 - j) = ");
        Serial.print(value);
        Serial.print(" * 10^(");
        Serial.print(buffer_length);
        Serial.print(" - 1 - ");
        Serial.print(j);
        Serial.print(") = ");
        Serial.println(value * pow(10, buffer_length - 1 - j));
        
        rate += value * pow(10, buffer_length - 1 - j);
      }
      Serial.print("Total: ");
      Serial.println(rate);
      buffer_length = 0;
    } else {
      buffer[buffer_length] = tmp;
      buffer_length++;
      Serial.print("buffer [");
      Serial.print(buffer_length - 1);
      Serial.print("] = ");
      Serial.println(buffer[buffer_length - 1]);
    }
  }
  for(i = 0; i < 256; i++) {
    analogWrite(PINOUT, i);
    delayMicroseconds(rate);
  }
  for(i = 255; i >= 0; i--) {
    analogWrite(PINOUT, i);
    delayMicroseconds(rate);
  }
}

Any idea what’s going on here?

hi, why don't you start simplifing program using atoi function ?

int atoi ( const char * str );

pow() is a floating point function liable to return values like 99.9999136 for pow(10.0, 2.0), which C will happily truncate to "99" when you try to put it in an integer variable. You can add 0.5 in appropriate places, but you really don't want to be using "pow()" if you're dealing only with integers.

I think the usual algorithm for doing this is something like:

total=0 total=total+hundredsDigit total=total*10 total=total+tensDigit total=total*10 total=total+onesDigit

and it works for any length of number. Just keep multiplying by 10 after adding each digit except the last.

Andrew

As westfw said, pow() deals with floats. You should declare "rate" and "value" as float, then the results may be more accurate and no roundings will take place.

then the results may be more accurate and no roundings will take place.

There is no such thing as a floating-point calculation where no roundings take place. The computing unit does not work in ratios or theoretical equations like you can do on paper, it works in a limited number of significant digits of precision.

If you call the pow() function, the compiler makes sure that the arguments are converted to floating point values automatically. (This is called type promotion, or implicit casting.) The pow() function will STILL give you a number that is not quite what your textbook says it should return.

The suggestion to use atoi() or atol() is correct, and those functions use the method Andrew explains to get their results.

Thanks for all the advice. It’s now correctly working with atoi. I didn’t know about atoi, but I’m glad it exists. My alternative was shakey with the pow function.

Works great, and less code:

#define PINOUT 9

void setup() {
  pinMode(PINOUT, OUTPUT);
  Serial.begin(115200);
}

int rate = 200;
char buffer[256];
int buffer_length = 0;

void loop() {
  int i = 0;
  if (Serial.available() > 0) {
    int tmp = Serial.read();
    if (tmp == 13) {
      rate = atoi(buffer);
      Serial.print("Changing rate to: ");
      Serial.println(rate);
      buffer_length = 0;
      memset(buffer, 0, 256); // clear the buffer
    } else {
      buffer[buffer_length] = tmp;
      buffer_length++;
    }
  }
  for(i = 0; i < 256; i++) {
    analogWrite(PINOUT, i);
    delayMicroseconds(rate);
  }
  for(i = 255; i >= 0; i--) {
    analogWrite(PINOUT, i);
    delayMicroseconds(rate);
  }
}

halley, The rounding I was talking about was when a float is assigned to an integer, as it is the case here:

        rate += value * pow(10, buffer_length - 1 - j);

Now, I don't understand what you mean by

There is no such thing as a floating-point calculation where no roundings take place

Obviously, you can have floating point calculation where there is no rounding, like 0.1 + 0.1 = 0.2. Rounding occurs only when the "number of significant digits of precision" would be exceeded (if you were to use paper and pen).

Funny you use that example, florinc.

Obviously, you can have floating point calculation where there is no rounding, like 0.1 + 0.1 = 0.2.

One tenth in decimal is easy to write. However, in binary, it’s really not so easy. It’s kind of like writing one third in decimal: 0.333333333333 with no end in sight.

[halley@halley11:/tmp]$ cat float.c
#include <stdio.h>
void main()
{
    float f = 0.1;
    float g = 0.1;
    float h = f + g;
    printf("%g\n", h);      // let C round it off so it looks purdy
    printf("%.20f\n", h);  // give twenty decimal places (more than the variable)
}
[halley@halley11:/tmp]$ float
0.2
0.20000000298023223877