How big is a long?

Confused!

Simple code....receives a single number from the serial console and multiplies it by 10,000.

    receivedChar=Serial.read(); //The character is read. It's 0-9
    Serial.println(receivedChar); // Check it's been received OK
    long asInteger=receivedChar -'0'; // Convert it to a number
    Serial.println(asInteger); //print the number out to check it's not changed
    long newInteger=10000*asInteger; //Multiply it by 10000 so it;s in the range 10000 to 90000
    Serial.println(newInteger); //print it out to check Arduino can do hard sums.

When character input is 1, this outputs 1 as the character, 1 as the 'asInteger' and 10,000 as the newInteger - perfect!

2 produces 20,000, and 3 as 30,000 as expected.

But 4 produces -25536.

Err....

long is supposed to be +/- 2 billion plus loose change?

If I replace ' long' with 'unsigned long' then it works.....all the way up to 90,000

What obvious thing am I completely missing here?

It's running on a nano (not that I can see why that would make a difference)

long newInteger=10000L*asInteger;

Ah! Many thanks!

There are many references, here is one of them.

Why int is not promoted to long automatically in this case?
EDIT: It is!

What board have you tried? I tested on Nano, no problem, no need to explicitly promote 10000 to 10000L

21:20:31.218 -> 49
21:20:31.218 -> 1
21:20:31.218 -> 10000
21:20:31.218 -> 50
21:20:31.218 -> 2
21:20:31.218 -> 20000
21:20:31.218 -> 51
21:20:31.218 -> 3
21:20:31.218 -> 30000
21:20:31.218 -> 52
21:20:31.218 -> 4
21:20:31.218 -> 40000
21:20:31.218 -> 53
21:20:31.218 -> 5
21:20:31.218 -> 50000

This is why I prefer the int32_t style, you know exactly what you are getting.

But int * long == long, doesnt matter that int here is 2 bytes, unless there is a bug in compiler int should have been promoted, Im very curious how did this happen

    long asInteger = 4;
    long newInteger=10000 * asInteger;

Has the code been changed? If not, why would multiplying an int by a long (10000 * asInteger) result in an int?!?

because the first operand determines the result type for the operation (* here)

m = 100 + 10000 * n
makes it more obvious. the expression is not evaluated starting from =, but starting from *

This makes no sense, in c++ there are rules for integer promotion, which stipulate if 2 numbers are signed integers, the one with lesser conversion rank is promoted to match the other one, ie int is promoted to long. Implicit conversions - cppreference.com

Please do include your source of confusion.

from " Usual arithmetic conversions":

  1. Otherwise, both operands are integers. Both operands undergo integer promotions

from "Integer promotions" section

integer promotion is the implicit conversion of a value of any integer type with rank less or equal to rank of int

usually int is a long int. but not on AVR. here int is a 16 bit and long is 32 bit

from " Usual arithmetic conversions":

  1. Otherwise, both operands are integers. Both operands undergo integer promotions (see below); then, after integer promotion, one of the following cases applies:

If the types are the same, that type is the common type.
Else, the types are different:
If the types have the same signedness (both signed or both unsigned), the operand whose type has the lesser conversion rank is implicitly converted to the other type.

:thinking:

What does that have to do with it?
Ranks will be aligned after integer promotion.

could you link this quote of yours?

  • If the types have the same signedness (both signed or both unsigned), the operand whose type has the lesser conversion rank 1 is implicitly converted2 to the other type.

This is why int is promoted to long, because int has lesser conversion rank than long. There is no need to explicitly convert 10000 to 10000L it works fine because it is promoted by the compiler according to the C++ promotion rules.

that quote is about signed/unsigned

you can argue with me how much you want. it doesn't change the fact that the compiler calculates it as int. try it with avr-gcc in Arduino AVR. only there is int 16 bit, on ARM int and long are both 32 bit and short (16 bit) is promoted to int (32 bit)

it would be interesting to test it for uint64_t for ARM

But after that, the two arguments to the '*' operator get converted to the 'common type'.

the operand whose type has the lesser conversion rank is implicitly converted to the other type.

'rank' is primarily bit size so a 16-bit 'int' has a lesser rank than a 32-bit 'long'. The '10000' should get converted to 'long' and the multiply is done in 'long'.

according to standard the promotion is only to int. not to larger types

i tried on nano, posted result, int promoted to long

I didn't say "promotion". I said "conversion".

As stated in the cppreference.com page referenced above:

Usual arithmetic conversions
The arguments of the following arithmetic operators undergo implicit conversions for the purpose of obtaining the common real type, which is the type in which the calculation is performed:

binary arithmetic *, /, %, +, -