Rules about different types arithmetic

Hello,
I didn't succeed to find documentation about the way numbers are computed when they are of different types.
From my experiments on an Arduino :

unsigned long a,b;
a=10;
b=a*1000*100;   // returns 1000000
b=1000*100*a;   // returns 4294656576

So :

  • expressions are evaluated from left to right
  • long × integer is evaluated as long

Then 1000*100L*a will be correctly evaluated.

1000*100*a will suffer from integer overflow

In this exemple from wifi library, there is the following formula, which convert seconds from 1970 to hours :

(epoch  % 86400L) / 3600

where epoch is an unsigned long

If I try :

unsigned long a,b;
a=4294967295;            // max unsigned long value
b=(a  % 86400L) / 3600;  // returns 6
b=(a  % 86400) / 3600;   // returns 6

Both formula return the same value. The 'L' doesn't seem necessary, as a%86400 should be evaluated first as a long integer, 'a' being a long integer.

Well If you have some links to the exact rules of cast / integer / types / arithmetic...

Google "c++ arithmetic conversion rules".

With most binary operators the narrower argument is widened to the size of the larger, and the result is cast back to the result variable type. By default integer constants are the int size, so L is necessary for longer constants
(although if an expression is compile-time optimized the constrain may not matter, but it never a sound
thing to leave the L out for long constants)

Intermediate results are not cast, only the final result, because it is the one that has to be stored
in the result variable.

int is 16 bits on many Arduinos, which catches a lot of people out - here 86400L is used because 86400 is
not a 16 bit int.

Well If you have some links to the exact rules of cast / integer / types / arithmetic...

we use ANSI C/C++

Mark

I indeed duckduckgo "c++ arithmetic conversion rules", but there is some points which puzzle me, mainly with constants.

If Arduino Uno do :

unsigned long b;
b=700001000;
b=70000L
1000;

I should have an overflow in the first case, as 70000 is larger than even an unsigned integer.
But the result is actually right in both case...

harlock974:
unsigned long b;
b=700001000;
b=70000L
1000;

I should have an overflow in the first case, as 70000 is larger than even an unsigned integer.
But the result is actually right in both case...

The compiler is smart enough to recognize an integer constant that won't fit in an 'int' and promote it to 'long'.

I know of one other thing the compiler fixes for you:

uint8_t low = 0x57;
uint8_t high = 0x34;
uint16_t result = high << 8 | low;

The compiler fixes it to make it work.

When writing code, show what your are doing:

unsigned long b;
b = 70000UL * 1000UL;

Then both the person reading the code and the compiler understand that it is a 'unsigned long' thing.

It can easily go wrong with a #define FACTOR 100000. Depending on how the define is used with other constants or other variables, it can be okay or it can go wrong.

Koepel:
I know of one other thing the compiler fixes for you:

uint8_t low = 0x57;

uint8_t high = 0x34;
uint16_t result = high << 8 | low;



The compiler fixes it to make it work.

In that case, high is promoted to int before it's shifted left because C++ does all integer operations with int or larger.