Data types and arithmetics

I'm not 100% sure that the subject it correct, but I think the trouble I'm having is relating to this.

Ok, so I'm writing a project to get temp and pressure from a BM085 sensor, but I just can't seem to get it to calculate the parametres correctly. For troubleshooting I followed an example in the datasheet (https://www.sparkfun.com/datasheets/Components/General/BST-BMP085-DS000-05.pdf, page 13), but I'm still not able to reproduce the results (only with datasheet, but not the arduino).

The sketch is basically something like this:

long UT = 27898;
unsigned short AC5 = 32757;
unsigned short AC6 = 23153;
short MC = -8711;
short MD = 2868;

void setup(){
  Serial.begin(9600);
  long X1 = (UT - AC6) * AC5 / (2^15);
  long X2 = (MC * (2^11)) /  (X1 + MD);

  Serial.print("X1 = "); 
  Serial.println(X1);
  Serial.print("X2 = "); 
  Serial.println(X2);
}

void loop(){
}

The serial.print returns the values "X1 = 11956305" and "X2 = 0". The values are expected to be 4743 and -2344 respectively.

I know this problem most certainly has a very obvious and simple solution, but I have to admit that I'm stuck here.

I suspect it's an order of operators issue

BODMAS

Try

  long X1 = ((UT - AC6) * AC5) / (2^15);

Also make sure your values don't overflow the long data type at any time during the individual computations on each line, I.e not just the result of the calculation which you are storing in a long

I think you can't do 2^15 in C. I usually do that with pow() function.

EDIT: Because 2^15 is not 2 XOR 15, right?

The "^" operator is bitwise XOR in C.

@Hackscribble good spot !

Just did some testing and 2<<(15-1) overflows, so the OP would need to use

  X1 = ((UT - AC6) * AC5) / (2L << (15-1));
  X2 = (MC * (2L<<(11-1))) /  (X1 + MD);

However there is a loss of precision in the result of X1 as the floating point result of that calculation is not an integer.

Normally I'd say refactor the second equation, but I'm not sure it would be possible to do that without overflowing the long data type.
Though I've not spent that long looking at the formula so it could be possible.

So if the OP needs it to give those values, I'd recommend either using a float or double, or possibly using a big numbers library

http://forum.arduino.cc/index.php/topic,85692.0.html

long UT = 27898;
unsigned short AC5 = 32757;
unsigned short AC6 = 23153;
short MC = -8711;
short MD = 2868;

You'll save yourself a lot of uncertainty by using the same data type (long) for all your constants. The calculations all have at least one long, so all of the ints will need promoted to longs anyway.
Both 2^11 and 2^15 are wrong.

 X1 = ((UT - AC6) * AC5) / (1<<16);  // Right shift "undefined" for signed numbers :-(
  X2 = ((long)MC<<12) /  (X1 + MD);

I assume that these calculations are from datasheets, so the bits discarded in making X1 are known irrelevant.

westfw:

long UT = 27898;

unsigned short AC5 = 32757;
unsigned short AC6 = 23153;
short MC = -8711;
short MD = 2868;



You'll save yourself a lot of uncertainty by using the same data type (long) for all your constants. The calculations all have at least one long, so all of the ints will need promoted to longs anyway.
Both 2^11 and 2^15 are wrong.


X1 = ((UT - AC6) * AC5) / (1<<16);  // Right shift "undefined" for signed numbers :frowning:
  X2 = ((long)MC<<12) /  (X1 + MD);



I assume that these calculations are from datasheets, so the bits discarded in making X1 are known irrelevant.

Using long for all constants and variables sounds like a good idea, I will implement that. maybe store them in an array. The exponential terms made a huge difference :). I'm not sure what you mean by the last part though. Does it have to do with overflowing?

I changed the code to:

  long X1 = ((UT-AC6_) * AC5_) >> 15; 
  long X2 = MC_ * (long)(1 << 11) / (X1 + MD_);

Which gives the correct results, however I had to type cast the "(1<<11)" term to "long" in order for it to work. I don't really understand why though (why didn't I have to type cast MC_ also?), since X2 being a 32bit integer should be able to store it?

You had to cast the value to long, because the compiler is treating the constants e.g. 1 as integers

A shorthand to make the compiler treat these constants as long is to put an L after the number

e.g. 1L

so the code becomes

long X1 = ((UT-AC6_) * AC5_) >> 15; 
  long X2 = MC_ * (1L << 11) / (X1 + MD_);

I don't really understand why though (why didn't I have to type cast MC_ also?), since X2 being a 32bit integer should be able to store it?

C has simple but counter-intuitive rules for doing math. Given an expression likelong X2 = MC * (1 << 11) / (X1 + MD);, it calculates results of sub-expressions "in order." So in 1<<11, both numbers are "int" (short) (all constants are
"int" unless you indicate otherwise), leading to a short result. Then we multiply by MC, which is another short, and expect another short result (which is now getting questionable.) X1 is a long, so X1+MD gets calculated using longs (MD is first "promoted" to a long), and produces a long result. The final division has the short result divided by a long, so again the short is convert to a long, the calculations are long, and you get a long result. ONLY THEN does it notice that the X2 is also a long, so it can directly assign the result of the calculations. The fact that the final destination is a long has NO EFFECT on calculating the value of the expression on the right hand side.

You can google "C promotion rules" and find out more. Note that this is a lot less obvious in many C compilers (desktop computers, for instance), because the default type for constants (int) is 32bits on those machines, and you don't do math that overflows 32bits nearly as often as you do math that overflows 16bits.

I think its also important to point out the Operator Precedence when doing calculations.

See

http://en.cppreference.com/w/cpp/language/operator_precedence

e.g.

void setup() {
 Serial.begin(115200);
 Serial.println(5+3 * 2);
}
void loop() {}

e.g. This prints 11 not 16 as the answer because the compiler does the multiply first

i.e 5 + (3*2)

Its also possible to overflow the middle of a calculation even if the end result should be well within the range of a long