doing math with a const variable

Hi. Quick question.
Why is this happening?
Seems like the microcontroller is internally using int to do the math because if I set "number" below 35 the result does not overflow.
How does the microcontroller choose which datatype to use for the calculation, please clarify.
Thanks

const byte number=35;

void setup() {
 
  Serial.begin(115200);
 

Serial.println(1000 * number);//____Result is -30536 OVERFLOW

//But if I do:
long x=number;
Serial.println(1000 * x);//_______Result is 35000

  //Why? There is no compiler warning. Yes I know that "number" is a declared as byte, but "number" never goes over 255 is stays at 35 const!

 
}

void loop() {

  
}

Both 35 and 1000 fit into a int16_t, so in the absence of any other indication of variable type that’s what it picks.

PerryBebbington:
Both 35 and 1000 fit into a int16_t, so in the absence of any other indication of variable type that's what it picks.

So how do I solve this if have no idea how large a number will be? Do I always have to set ALL math equation variables [A,B, AND result] to the largest datatype that or just the result variable?
Is there any way to set the default datatype the microcontroller uses for math calculations so I just need the "result" variable to be let's say "long" and not the other variables?
Why doesn't the microcontroller see that it overflows and use a large datatype internally to do the calculation?

So even in this case below I have to foresee what the largest outcome can possibly be and set not only the "result" but also "a" and "b" to that larger datatype?

int a;
int b;
long result;

void setup() {
 
  Serial.begin(115200);

 a=35;
 b=1000;
 result=(a*b);
 Serial.println(result);// I got -30536
}

void loop() {

  
}

Since you're multiplying a literal constant by a const variable, it's probably not the "microcontroller" making the choice. It's the compiler doing the math at compile-time. Try:

Serial.println(1000UL * x);

So how do I solve this if have no idea how large a number will be?

You are the one writing the program, you are the only person who has any idea what data will come into the program from whatever it interfaces with, you should have some idea of the possible range of any variables.

The compiler doesn't have a clue.

gfvalvo:
Since you're multiplying a literal constant by a const variable, it's probably not the "microcontroller" making the choice. It's the compiler doing the math at compile-time. Try:

Serial.println(1000UL * x);

my last example is not a const

When you multiply 2 int data types or unknown magnitude, you have to know that the product can be larger than an int data type can hold. So plan for it.

result=long(a)*b;

PerryBebbington:
You are the one writing the program, you are the only person who has any idea what data will come into the program from whatever it interfaces with, you should have some idea of the possible range of any variables.

The compiler doesn't have a clue.

So to clarify, you are saying that ANY variable that has the potential to interface with ANY other variable at ANY part of a given math equation and may exceed the datatype while solving the equation EVEN if it is not the final output of the math problem must be declared at that larger data type?

So to clarify, you are saying that ANY variable that has the potential to interface with ANY other variable at ANY part of a given math equation and may exceed the datatype while solving the equation EVEN if it is not the final output of the math problem must be declared at that larger data type?

Pretty much, if I understood you correctly, yes. You have to think about what might happen and write your code accordingly.

How could it be otherwise?

Quote from: PerryBebbington Wed Nov 11 2020 14:39:05 GMT-0500 (Eastern Standard Time)

Pretty much, if I understood you correctly, yes. You have to think about what might happen and write your code accordingly.

How could it be otherwise?

How could it be otherwise?

Here is an example!
Now this is interesting, can you explain this?

int a;
int b;
long result;

void setup() {
 
  Serial.begin(115200);

 a=35;
 b=1000;
 result=(a*b);
 Serial.println(result);// result = -30536 OVERFLOW

}

void loop() {

  
}

BUT Take a look at this!
If I do the same thing with "byte" it does NOT overflow!!
If what you are saying is correct.. what gives?

byte a;
byte b;
long result;

void setup() {
 
  Serial.begin(115200);

 a=255;
 b=2;
 result=(a*b);
 Serial.println(result);//_I got result= 510!!
}

void loop() {

  
}

This is a double standard, does it overflow or not?

If I do the same thing with "byte" it does NOT overflow!!
If what you are saying is correct.. what gives?

Take a deep dive into the pool called "integer promotion"

https://en.cppreference.com/w/c/language/conversion

On most arduinos, calculations default to 16-bit integers. If you expect the result to exceed that, cast one of the numbers to a 32-bit type (long or unsigned long). The variables do not need to be long.

cattledog:
Take a deep dive into the pool called "integer promotion"

Implicit conversions - cppreference.com

Will definitely take a look! Thanks!

Today at 07:58 pm Last Edit: Today at 07:59 pm by david_2018
On most arduinos, calculations default to 16-bit integers. If you expect the result to exceed that, cast one of the numbers to a 32-bit type (long or unsigned long). The variables do not need to be long.

I understood that all along. "result" WAS cast as a "long". I guess you are saying that "a" OR "b" must be cast as "long"?

I guess you are saying that "a" OR "b" must be cast as "long"?

See reply #7.

david_2018:
On most arduinos, calculations default to 16-bit integers. If you expect the result to exceed that, cast one of the numbers to a 32-bit type (long or unsigned long). The variables do not need to be long.

Just checked. Does not hold true.
Check this out.
I have declared "c" as "long"
In Example 1 it does not overflow.
In example 2 it sure does.
Do I need to declare all of them as long?

int a;
int b;
long c;
long result;

void setup() {
 
  Serial.begin(115200);

 a=35;
 b=1000;
 
 result=(a*(b+c));
 Serial.println(result);//result = 3500 NO OVERFLOW
}

void loop() {

  
}
int a;
int b;
long c;
long result;

void setup() {
 
  Serial.begin(115200);

 a=35;
 b=1000;
 
 result=(a*b+c);
 Serial.println(result);//result = -30536 OVERFLOW !
}

void loop() {

  
}

and "c" is ZERO.

?

when you do

result=(a*b+c);

a and b being int, the expression a*b is evaluated as int and the compiler overflows
Adding c later would get the left member of the addition promoted but it’s too late, damage is done.

doing   result = ((long)a * b + c);would lead to the correct answer
a is promoted to long, so b will also be promoted to long in order to perform the multiplication, then you add two long int together, so all is fine

read also about evaluation rules, sub-expressions can be evaluated in random order, not always left to right. ie if you do (AB)+(CD), the C++ specification does not guarantee that (AB) will be evaluated before (CD)

This is all rigidly defined by the language standard. If you want definitive answers, look there. You're not plowing any new ground here.

J-M-L:
when you do

result=(a*b+c);

the compiler overflows when doing a*b, adding c later would get the expression promoted but it’s too late.

Got it! Thanks!