unsigned long const, literal, expression give different results?

What have I overlooked here, folks?

I ran into this working on an ATtiny85 sketch (where one is almost totally blind).
I ported it to ATmega328 (UNO) so I can use serial to debug.

Here’s a minimal sketch that shows the problem:

#include <Arduino.h>

const unsigned long BITRATE = 115200; // my fav.

void printThis(unsigned long x) {

  Serial.println(x);
  
}

void setup() {
const unsigned long BIG = 120000;

  Serial.begin(BITRATE);
  Serial.println("Reset.");

  Serial.print("Literal: ");
  printThis(120000);
  Serial.print("Const: ");
  printThis(BIG);
  Serial.print("Expression: ");
  Serial.print(120*1000);

  while(1) {
    ;           // wait here.
  }
}

void loop() {
  ;   // never called, never runs.
}

This code should print the same number (120,000) all three times. It doesn’t.
The result of 120*1000 is apparently not an unsigned long, but signed.

And, before you ask, yes, I have played about with casts (which, oughtn’t, IMO, be needed).

I have also taken the precautions recommended (empty sketch, source in .cpp).

I haven’t tried this, but if the call to ‘printThis()’ were replaced with ‘delay()’, I predict you’d be waiting a very long time!

The workaround is of course obvious. I’m wondering if this is a bug or expected behavior.

best regards,
Daniel

 Serial.print(120ul*1000ul);

The result of 120*1000 is apparently not an unsigned long, but signed.

Of course it is. 200 is an int (signed). So is 1000. Why would you expect the result of multiplying an int by an int to be anything other than an int?

The result of 120*1000 is apparently not an unsigned long, but signed.

The result of multiplying an int by an int is an int

Try this

Serial.print(120UL * 1000);

I expect the previously defined subroutine to inform the compiler that an unsigned long value is expected.

I mean, try passing "120,000" and see what happens! (Although I would expect 120000.0 to work.)

And, perhaps I was not clear: I'm not really attempting to print a big number. The problem showed up in a call to a delay routine (my own), which expects an unsigned long argument (just like delay). Passing a signed long (in error) produces a very long delay.

Google " automatic type promotion in c"

UKHeliBob:
The result of multiplying an int by an int is an int

But that isn't really the actual problem in this case, so it isn't too meaningful to the OP.

The problem is the resulting int is bigger than the size an int can represent on the processor being used.
i.e. on the AVR an int is only 16 bits and the result of 1201000 is to big to fit in 16 bits. (it overflows)
and the results and expected behavior when overflow occurs is undefined.
However it is typically handled the same way in various implementations in that the result is the lower bits that fit within the size of an int.
120
1000 is 120000 or x1d4c0 and the lower 16 bits is 0xd4c0 which as a 16 bit int is -11072

In the case of calling a function that expects unsigned long,
the signed integer result of the calculation ( -11072 ) is then promoted/converted to an unsigned long which is what the function expects, however the result of converting a negative integer to an unsigned long is a very big number.

This is a good example of how turning on the compiler warnings would be helpful.
If all warnings were enabled, which is the only way I ever compile code, (and on my own non Arduino projects I treat warnings as errors), you would have seen a warning like this:

test.ino:22:19: warning: integer overflow in expression [-Woverflow]
   Serial.print(120*1000);
                   ^

I'd suggest turning on all compiler warnings so you can see these types of issues.

--- bill