Calculating an angle from a binary value - question about casting

Hi folks!

I'm using arduino code to calculate angles based on ten bit binary values (of type 'unsigned int').

The angle variables I use are of type 'float'.

When I do a straight calculation without re-casting the binary value, I notice that I don't get the 'correct' result for the calculated angle. What I mean by 'correct' result is - it's not the result that I'm after. I realise that the code is just doing what it's told to do - so it's my inadequate understanding of what's going on.

Now, when I do a different calculation by re-casting the binary value to a 'float' or a 'unsigned long', I notice that the angle value is the one that I want.

Could anyone help me with understanding the difference in results?

I've included my short arduino code - which runs on my MEGA2560, but it should run on UNO as well.

And I'll show the results that I was mentioning as well. Thanks for any help in advance!!

// Calculating an angle (of type float) using a binary coded value (decimal value 729).
// The binary coded value is initially of type "unsigned int" (UNsigned)

// Results get printed in the 'Serial Monitor' (at baud rate 9600)

// Three results are shown. The first result is differnt to the second and third result.

// The first result (0.29 degree) is straight calculation without re-casting the binary value.
// The second result (256.29 degree) is the result of re-casting the value to a (float)
// The third result (also 256.29 degree) is the result of re-casting the value to (unsigned long)

unsigned int value729 = 0b1011011001; // represents decimal value 729

float angle1 = 0;
float angle2 = 0;
float angle3 = 0;

String mystring = "";


void setup() {

  Serial.begin(9600);
  
  angle1 = value729 * 360/1024.0;
  angle2 = (float)value729 * 360/1024.0;
  angle3 = (unsigned long)value729 * 360/1024.0;


  mystring = String(angle1);
  Serial.print(mystring);  Serial.println(" deg");

  mystring = String(angle2); 
  Serial.print(mystring);  Serial.println(" deg");

  mystring = String(angle3); 
  Serial.print(mystring);  Serial.println(" deg");


}

void loop() {

}

Serial Monitor results:

0.29 deg
256.29 deg
256.29 deg

Evidently the appearance of float 1024.0 in the first calculation does not result in promotion of all variables on the right hand side to float, as I expected.

This works as it should (I got rid of the useless String conversions):

// Calculating an angle (of type float) using a binary coded value (decimal value 729).
// The binary coded value is initially of type "unsigned int" (UNsigned)

// Results get printed in the 'Serial Monitor' (at baud rate 9600)

// Three results are shown. The first result is differnt to the second and third result.

// The first result (0.29 degree) is straight calculation without re-casting the binary value.
// The second result (256.29 degree) is the result of re-casting the value to a (float)
// The third result (also 256.29 degree) is the result of re-casting the value to (unsigned long)

unsigned int value729 = 0b1011011001; // represents decimal value 729

float angle1 = 0;
float angle2 = 0;
float angle3 = 0;

void setup() {

  Serial.begin(9600);

  angle1 = value729 * 360./1024.0;
  angle2 = (float)value729 * 360/1024.0;
  angle3 = (unsigned long)value729 * 360/1024.0;

  Serial.println(value729);

  //  mystring = String(angle1);
  Serial.print(angle1);  
  Serial.println(" deg");

  //  mystring = String(angle2);
  Serial.print(angle2);  
  Serial.println(" deg");

  //  mystring = String(angle3);
  Serial.print(angle3);  
  Serial.println(" deg");


}

void loop() {

}

Just as I would expect.

angle1 = value729 * 360/1024.0;

Convert 360 (an int) to an unsigned int, and multiply by the unsigned int value729 And USE 16 BIT ARITHMETIC! (This means subtract out 65536 repeatedly until you get a result less than 65536. This is called modulo arithmetic.)
Now convert to a floating point value and divide by the floating point number 1024.0
As expected, the result is 0.2890625

angle2 = (float)value729 * 360/1024.0;

Convert the unsigned int value729 to a float, multiply by the float 360.0, and divide by the float 1024.0
Result is 256.2890625

angle3 = (unsigned long)value729 * 360/1024.0;

Convert the unsigned int value729 to an unsigned long, convert the int 360 to an unsigned long, multiply them using 32 bit arithmetic, convert the result to a floating point value, and divide by the float 1024.0
Result is 256.2890625

Suggestion: Either change the 360 to 360.0, or declare value729 as an unsigned long (instead of unsigned int) in the first place.

Note: the specification of 16 bits and 32 bits is specific to 8 bit Arduinos. Other processors may differ.

The rules for promotion of data type are somewhat obscure and hard to remember. I had forgotten the left-to-right rule.

From C Class - Promotion, Conversion and Casts

conversion spreads from left to right to raise an expression to the largest datatype:

in an entirely int context, 40 / 17 * 13 / 3 would evaluate to 8 (40 / 17 rounds to 2, 2 * 13 = 26, 26 / 3 rounds to 8 )
to evaluate 40 / 17 * 13 / 3.0
40 / 17 again rounds to 2
2 * 13 = 26
but the 3.0 forces the final division into a double context and thus 26.0 / 3.0 = 8.666...

jremington and vaj4088 ..... thanks to both of you for your time and help!

I'll certainly learn a lot from all this, and will pass this great knowledge along.

There are lots of times I do something in coding, but don't understand what's happening in the wings, so I'm thinking that if I don't ask the experts (that's you guys), I'll just keep doing certain things blindly - like using what I find to work, but not doing things properly (like learning what's going on underneath).

The details you both gave are incredibly helpful, and useful.

Thanks for helping me with this. Greatly appreciated!! I'll be remembering these things you taught me, and will be able to show others too if they come across the same question!