Author Topic: Arduino math errors  (Read 1203 times)

0 Members and 2 Guests are viewing this topic.

Offline metrologistTopic starter

  • Super Contributor
  • ***
  • Posts: 2253
  • Country: 00
Arduino math errors
« on: March 04, 2018, 08:10:01 pm »
[edit] I suspect analogRead returns an integer value and then does integer math.

I'm curious why the following calculations produce different results?

Code: [Select]
float volts = 0;
float v = 0;
float v1 = 0;
float v2= 0;
float time = 1;
float count = 0;

void setup() {
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  Serial.begin(9600);
}

void loop() {
  count=count+1;
  volts = volts + (analogRead(A1) * 5 / 1023); //if "5" is changed to "5.0" it works
  if (millis() - time >= 1000){
    volts = volts/count;
    v = analogRead(A1);
    v1 = 5.0*(analogRead(A1)/1023);
    v2 = 5.0/1023*analogRead(A1);
    Serial.print(v*5/1023);
    Serial.println("V");
    Serial.print(v1);
    Serial.println("V1");
    Serial.print(v2);
    Serial.println("V2");
    Serial.print(volts);
    Serial.println("Volts");
    Serial.println(".");
    count = 0;
    time = millis();
  }
}



I feed in 2.5 volts and get this output:
2.70V
0.00V1
2.70V2
2.00Volts
.
2.71V
0.00V1
2.71V2
2.00Volts


Also, the voltage is fairly off, maybe my USB voltage is low (yes, 4.6 volts).

I searched around and find many old posts about more general math operations, and such as http://en.cppreference.com/w/cpp/language/operator_precedence
« Last Edit: March 04, 2018, 08:16:41 pm by metrologist »
 

Offline agehall

  • Frequent Contributor
  • **
  • Posts: 390
  • Country: se
Re: Arduino math errors
« Reply #1 on: March 04, 2018, 08:19:07 pm »
Let's focus on the V1 calculation as that is where things go wrong.

Essentially, what you are saying when you ask the compiler to compile "5.0/(analogRead(A1)/1023)" is "Divide the float 5.0 by the result of (reading an integer via analogRead() divided by the integer 1023).

The key here is that analogRead() returns an integer between 0 and 1023. Dividing that result by 1023 is 0 as long as the result of analogRead() isn't 1023 as that is how integer division in C works.

I won't go into details of other errors you might introduce when multiplying/dividing numbers on a computer but the subject is quite fascinating imho and one of the courses I took a long time ago that gave me much more use than I had ever expected.
 

Offline madires

  • Super Contributor
  • ***
  • Posts: 8183
  • Country: de
  • A qualified hobbyist ;)
Re: Arduino math errors
« Reply #2 on: March 04, 2018, 08:28:27 pm »
Yep, adding explicit type conversion will fix the issues.
 

Offline Kelbit

  • Regular Contributor
  • *
  • Posts: 58
  • Country: ca
Re: Arduino math errors
« Reply #3 on: March 04, 2018, 08:33:40 pm »
analogRead() returns type int. So, look at this line:

Code: [Select]
volts = volts + (analogRead(A1) * 5 / 1023); //if "5" is changed to "5.0" it works
Say you in feed 2.5V, for a 5V reference voltage you'll get about mid-scale (512). 512 * 5 is performed as an integer multiplication, yielding another integer value of 2560. 1023 is also an integer, so 2560/1023 is performed as an integer division. An integer cannot represent 2.5, it can only represent whole numbers. By the convention in the C standard, the quotient (floor) is returned from the division, so you get 2 as an output, which is then cast to a float (2.00).

The reason it works if you change the 5 to 5.0 is because the first multiplication is now 512 (int) * 5.0 (float), which creates an implicit cast, evaluating the whole calculation in floating point.

To be perfectly clear as to what sort of operations are happening, I would have personally written the line like so:

Code: [Select]
volts = volts + ( (float) analogRead(A1) * 5.0 / 1023.0);
The constants are explicitly floating-point, and the analogRead value has been explicitly cast to float. This way, it's now clear that everything is operating in floating point, as you intend it.

Your v1 calculation has a similar problem:
Code: [Select]
v1 = 5.0*(analogRead(A1)/1023);
In this case, the division in the brackets is evaluated first, which is an int/int division. Since 0 <= analogRead <= 1023, the division evaluates to less than 1 for all values of analogRead less than 1023. As we learned before, integer division returns the quotient (ie: the floor), so this will always come out to 0 for values of analogRead less than 1023, and 1 if analogRead is exactly equal to 1023. This integer value is implicitly cast to a float when it is multiplied by 5.0, however, it is already either 0 or 1, so v1 will always be either 0.0 or 5.0

As for your measured voltage being incorrect, yes, that would certainly be the case if your board voltage is 4.6V. Either use a stable 5V supply, or use a dedicated reference for the ADC.
« Last Edit: March 04, 2018, 08:35:15 pm by Kelbit »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf