Go Down

Topic: Equation giving different results (Read 466 times) previous topic - next topic

Jmok

Hi there, I am using an Arduino Uno and a Motor Shield to control a DC motor with an encoder. To calculate the RPM's of the motor I use:

RPM_A = (60*(newpositionA - oldpositionA) * 1000) /((newtimeA-oldtimeA)*16*2);

This equation works perfectly fine when going in the positive direction, once I start going in the negative direction it says its rotating at ridiculously high positive speeds. So I broke the equation into:
part1 = 60*(newpositionA - oldpositionA) * 1000;
part2 = (newtimeA-oldtimeA)*16*2;
part3 = part1/part2;

Where part 3 should equal RPM_A, strangely I receive the proper result, I do not understand what is different between the two approaches, mathematically they are the same but through arduino it is different. newposition and oldposition are variable long, newtimeA & oldtimeA are unsigned long, and RPM_A, part1,part2,part3 are double.

econjack

#1
Jul 15, 2014, 03:24 pm Last Edit: Jul 15, 2014, 03:28 pm by econjack Reason: 1
In your equation:

Code: [Select]

RPM_A = (60*(newpositionA - oldpositionA) * 1000) /((newtimeA-oldtimeA)*16*2);


how is RMP_A defined? If newpositionA - oldpositionA is as small as 1, the numerator for the expression will overflow an int. I'm pretty sure the variable needs to be a long. I'm only guessing since you didn't post your code.

edit:

I see now that you have it as a long. If RPM_A is a double, which is not supported by the Arduino IDE, change it to a float. Also, by default, the compiler will treat numeric constants as int. Try:

Code: [Select]

RPM_A =(float) (60.0 * (newpositionA - oldpositionA) * 1000.0) /((newtimeA-oldtimeA)*32.0);


and see what happens.

Peter_n

You can do all kind of in-between-calculations. The compiler will make the same code.

Code: [Select]

// multiply by 60 because ... ?
// multiply by 1000 because ... ?
long t1 = 60L * 1000L *(newpositionA - oldpositionA);
// multiply by 16 because ... ?
// multiply by 2 because ... ?
long t2 =16L * 2L * (newtimeA-oldtimeA);
// Calculate the rpm
RPM_A = (float) t1 / (float) t2;

James C4S


I do not understand what is different between the two approaches, mathematically they are the same but through arduino it is different.

Not different in the Arduino, but different to the compiler.  When different variable types are mixed, the compiler sometimes make a bad guess on how to handle the overall calculation.  Keep in mind that numbers like "60" are constants and usually, the compiler will treat them as an integer (int).  So you're mixing ints, longs, and floats (double doesn't exist in AVR).

As already suggested:
1.  Keep the intermediate steps.  They'll likely get optimized out.  Personally this is my method because it allows me to visually see the variable types and their expected results when I scan through the code.

2.  Convert your integers into floats, so they get treated as floats (or as long).  The simplest way is adding the decimal.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

el_supremo

Quote
the compiler sometimes make a bad guess on how to handle the overall calculation.


The compiler doesn't guess.

Pete

Jmok

Thanks for the feedback,

I didn't realize double wasn't part of the Arduino IDE, I changed it to float and added decimal places to the numbers and it works.

KeithRB

It is not that double is not a part of the IDE, it is just that floats and doubles are the same size.

Peter_n


Quote
the compiler sometimes make a bad guess on how to handle the overall calculation.


The compiler doesn't guess.

Pete


Pete, that was the funniest reply I read in a while.
I guess the compiler doesn't have to guess, because it "feels" what to do  :D

Robin2


Quote
the compiler sometimes make a bad guess on how to handle the overall calculation.


The compiler doesn't guess.

Pete



Perhaps it was the guys who wrote it that did the guessing.

...R

el_supremo

The language specification defines how a compiler must handle things like precedence of operators (e.g. a*b + c) and mixed mode operations (e.g. the result of an int * float) and all the other aspects of the language.
There would hardly be any point trying to write a program if the first time you compiled the statement "a = b/5;" the compiler guessed that the 5 was an integer and the next time it guessed that it was a float.

Pete

Robin2


guessed that the 5 was an integer and the next time it guessed that it was a float.


Pete, you are taking us too literally.

Of course the copiler doesn't guess. But if the people writing code (me for example) don't know the precise compiler rules we may "guess" wrong about what the compiler will do. The end result is pretty much the same as if the compiler was doing the guessing because we know what we wanted to happen (we would have got the correct answer with a calculator) - but it doesn't happen.

...R

el_supremo

The problem is that a newbie might interpret "the compiler guessed wrong" literally. I would have had no problem if the statement had been "the programmer guessed wrong".

Pete

James C4S

I think we've visited this pedantic rat-hole for long enough.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

GoForSmoke

You can use casting to do the integer calculation in 32 bit and safely put the result into 16 bit.
This avoids the occasional .999999 is 1 floating point funnies and run a magnitude or two faster than float on AVR.

Code: [Select]

int RPM_A;

..........

RPM_A = (int) ( 60L * ((long) newpositionA - (long ) oldpositionA ) * 1000L ) / (((long) newtimeA - (long) oldtimeA ) * 16L * 2L );

Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

el_supremo

@James C4S: Rather than acknowledge that your statement was wrong and move on, your first response on the issue is an ad hominem attack. Classy.

Pete

Go Up