Equation giving different results

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)162);

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)162;
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.

In your equation:

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:

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

and see what happens.

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

// 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;

Jmok:
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.

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

The compiler doesn't guess.

Pete

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.

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

el_supremo:

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 :smiley:

el_supremo:

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

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

el_supremo:
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

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

I think we've visited this pedantic rat-hole for long enough.

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.

int RPM_A;

..........

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

@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

If you don't get inside the compiler (few do, I certainly haven't) then it's a black box you can't be sure of in all conditions.

That's why it is up to the programmer to use type suffixes on constants where applicable and to cast variables to types to avoid mixed math or math that may overflow. In Forth code there are 'commands' that promote operands as part of the operation, the scaling operator ( */, 1st val * 2nd val / 3rd val scales the 1st ) does that.

It's up to the programmer because the compiler you have is the compiler you deal with.
Be smart or take chances to be tracking down extra bugs just because you're lazy or stubborn.

If you think "inconsistent compiler" then you might be more definite in your code even if that idea is a bit daft.
If you think "inconsistent programmer me" then you might take the time to learn more/better.
Maybe it's better to do both.