Arduino math errors - Page 1
Forum section: electronics / beginners

author: metrologist
author_rank: Super Contributor
timestamp: March 04, 2018, 08:10:01 pm
content: I suspect analogRead returns an integer value and then does integer math.I'm curious why the following calculations produce different results?Code: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 worksif (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.70V0.00V12.70V22.00Volts.2.71V0.00V12.71V22.00VoltsAlso, 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 ashttp://en.cppreference.com/w/cpp/language/operator_precedence

author: agehall
author_rank: Frequent Contributor
timestamp: March 04, 2018, 08:19:07 pm
content: 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.

author: madires
author_rank: Super Contributor
timestamp: March 04, 2018, 08:28:27 pm
content: Yep, adding explicit type conversion will fix the issues.

author: Kelbit
author_rank: Regular Contributor
timestamp: March 04, 2018, 08:33:40 pm
content: analogRead() returns type int. So, look at this line:Code:volts = volts + (analogRead(A1) * 5 / 1023); //if "5" is changed to "5.0" it worksSay 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 anotherintegervalue 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: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: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.0As 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, oruse a dedicated referencefor the ADC.