I am confused

unsigned long x = 255 * 16777216 + 255 * 65536 + 255 * 256 + 255;

Serial.println(String(x));

4294901759

Should that not be 4294967295?!?

No - the compiler is right your wrong.

Why because at no point on the LHS (left hand side of the =) have you told it to use more than a 16 bit int.

Mark

now I am even more confused :confused:

form the docs...

unsigned long

Description

Unsigned long variables are extended size variables for number storage, and store 32 bits (4 bytes). Unlike standard longs unsigned longs won't store negative numbers, making their range from 0 to 4,294,967,295 (2^32 - 1).

the returned number is much larger than an int

because the largest number used on the RHS is 24 bits then x will contain a maximum of 24 bits

is that correct?

First we deal with the LHW and then and only then do we look at the RHS

Mark

because the largest number used on the RHS is 24 bits

16 bits NOT 24!

Mark

Try:

void setup() {

  unsigned long x = 255L * 16777216L + 255L * 65536L + 255L * 256L + 255L;

  Serial.begin(9600);
  Serial.println(String(x));
}

void loop() {

}

@Mark: Not sure what you mean by:

First we deal with the LHW and then and only then do we look at the RHS

I am unfamiliar with the L appended to the values but if I understand what is happening then the L should not be necessary on the final 4 values

time to dig through the docs some more

thank you for you attention

@mark, what I said made no sense because I did not understand the root of the problem and your comments only served to confuse me more

in simple terms the compiler didn't know you wanted to work the whole sum in unsigned long. It knew you wanted the answer in unsigned long but it stored the numbers as int. This caused them to roll over and gave you a result that you wasn't expecting

By adding a L you told the compiler that this number is a long

If you have read the manual you will find C++ maths a little odd as its not read left to right its based on the *+-/ kind of order unless you specify which order to do the calculation so adding L on to 255 is a grey area (at least for me) so no harm in adding it just in case.

gpop1:
...so adding L on to 255 is a grey area (at least for me)...

Nothing gray about. The exact order of evaluation is precisely, and rigidly, defined. But, for any but simple expressions, it's a lot easier, and safer, to simple always parenthesize, unless you are certain you can properly remember the order. If there is potential for an overflow, dding an L to an operand will never hurt you, but leaving one out often will.

Regards,
Ray L.

See integer arithmetic and overflow.

RayLivingston:
Nothing gray about. The exact order of evaluation is precisely, and rigidly, defined

...in a way that probably made perfect sense in the early 1970's, but not today.
Hence the OP's question.

odometer:
...in a way that probably made perfect sense in the early 1970's, but not today.
Hence the OP's question.

What on earth makes you think it is less well-defined now than it was in the '70s? It has never changed.... The OPs question was brought on because he had no clue how expressions are evaluated.

Regards,
Ray L.

I tried reading the Microsoft document on operator precedence and order of evaluation but did not grock much

@nick gammon, after reading your explanation it became much clearer

this works

Serial.println(String((unsigned long) 255 * 16777216 + 255 * 65536 + 255 * 256L + 255));

It appears to me the expression is evaluated from right to left, performs the multiplies before the additions and also calculates the entire expression in the size of the first math operation encountered

Is this right? it is on your page

Casting is useful for variables, because you can't just add "L" to the end of a variable name.

should that not be 'useless'?

thank you for your help

Casting is useful for variables, because you can't just add "L" to the end of a variable name.

should that not be 'useless'?

No, it is OK as it stands.

With a literal (eg. 42) you can add L (eg. 42L).

With a variable (eg. FOO) if you add L you get FOOL - which is a different variable.

So for a variable, casting can be done, eg.

long (foo)

or:

(long) foo

Serial.println(String((unsigned long) 255 * 16777216 + 255 * 65536 + 255 * 256L + 255));

It appears to me the expression is evaluated from right to left, performs the multiplies before the additions and also calculates the entire expression in the size of the first math operation encountered

No it's not right to left, it is BODMAS. Order of operations - Wikipedia.

The first cast ((unsigned long) 255) promotes the expression to unsigned long. Then as the compiler works its way along it still uses BODMAS.

Try this, for example:

  Serial.println(255 * 8388608 );  // prints: 2139095040

8388608 is 223.

8388608 can fit into a signed long, so that promotes the expression to signed long, and 255 * 8388608 also fits into a signed long.

Make it 224 though (16777216) and it doesn't work.

  Serial.println(255 * 16777216 );  // prints: -16777216

16777216 fits into a signed long still, so the expression is now signed long (not unsigned long). However the result (4278190080) doesn't fit into a signed long, but the compiler doesn't care about that.

Tell the compiler "hey, I want this to be unsigned" and you get the correct answer:

  Serial.println(255 * 16777216U );  // prints 4278190080

... and also calculates the entire expression in the size of the first math operation encountered ...

No, it can promote the expression as it goes, as I showed in the example above.