Skip to main content
added 238 characters in body
Source Link
Majenko
  • 105.9k
  • 5
  • 82
  • 139

This is not a bug. It's just that you are working with signed 16 bit integers on an 8 bit microcontroller. The maximum an int can store is 32767, and literals are 16 bit signed by default.

Your three prints are:

  Serial.println(feedLimit);

Print 30.

  Serial.println(feedLimit*60*1000);

Multiply feedLimit by 60 = 1800. Multuply 1800 by 1000 = 1800000. Fit 1800000 into 16 bit signed = 30538. Also works the other way: preprocessor multiplies two 16 bit ints to create a new 16 bit int constant: 60 * 1000 = 60000, which is too big to fit in an int and truncates to 27232. Multiply by 30 = 30538 when truncated to 16 bits.1

  Serial.println(feedLimit*60000);

Multiply 30 by 60000. 60000 is too big to store in an int so it is implicitly promoted to a long. 30 * 60000 = 1800000. The result is a long because you have an implicit long in your calculation.

You have to take care with big numbers on small microcontrollers and keep in mind the limits of each variable type, along with what the default data type for literals is.

  • char: -128 to +127
  • unsigned char / byte: 0 to 255
  • int: -32,768 to +32,767
  • unsigned int: 0 to 65,535
  • long: -2,147,483,648 to 2,147,483,647
  • unsigned long: 0 to 4,294,967,295

You can force literals to be a specific data type by adding a suffix to them:

  • L: Long
  • UL: Unsigned long

(There are others too but less often used).

So your first (failed) calculation can be fixed by forcing one of the values to be a long:

  Serial.println(feedLimit*60*1000L);

1: It seems avr-gcc isn't clever enough to do this, instead it's always treating each operation separately, and each time it works from int-to-int using two 8-bit registers to represent each value or result.

This is not a bug. It's just that you are working with signed 16 bit integers on an 8 bit microcontroller. The maximum an int can store is 32767, and literals are 16 bit signed by default.

Your three prints are:

  Serial.println(feedLimit);

Print 30.

  Serial.println(feedLimit*60*1000);

Multiply feedLimit by 60 = 1800. Multuply 1800 by 1000 = 1800000. Fit 1800000 into 16 bit signed = 30538. Also works the other way: preprocessor multiplies two 16 bit ints to create a new 16 bit int constant: 60 * 1000 = 60000, which is too big to fit in an int and truncates to 27232. Multiply by 30 = 30538 when truncated to 16 bits.

  Serial.println(feedLimit*60000);

Multiply 30 by 60000. 60000 is too big to store in an int so it is implicitly promoted to a long. 30 * 60000 = 1800000. The result is a long because you have an implicit long in your calculation.

You have to take care with big numbers on small microcontrollers and keep in mind the limits of each variable type, along with what the default data type for literals is.

  • char: -128 to +127
  • unsigned char / byte: 0 to 255
  • int: -32,768 to +32,767
  • unsigned int: 0 to 65,535
  • long: -2,147,483,648 to 2,147,483,647
  • unsigned long: 0 to 4,294,967,295

You can force literals to be a specific data type by adding a suffix to them:

  • L: Long
  • UL: Unsigned long

(There are others too but less often used).

So your first (failed) calculation can be fixed by forcing one of the values to be a long:

  Serial.println(feedLimit*60*1000L);

This is not a bug. It's just that you are working with signed 16 bit integers on an 8 bit microcontroller. The maximum an int can store is 32767, and literals are 16 bit signed by default.

Your three prints are:

  Serial.println(feedLimit);

Print 30.

  Serial.println(feedLimit*60*1000);

Multiply feedLimit by 60 = 1800. Multuply 1800 by 1000 = 1800000. Fit 1800000 into 16 bit signed = 30538. Also works the other way: preprocessor multiplies two 16 bit ints to create a new 16 bit int constant: 60 * 1000 = 60000, which is too big to fit in an int and truncates to 27232. Multiply by 30 = 30538 when truncated to 16 bits.1

  Serial.println(feedLimit*60000);

Multiply 30 by 60000. 60000 is too big to store in an int so it is implicitly promoted to a long. 30 * 60000 = 1800000. The result is a long because you have an implicit long in your calculation.

You have to take care with big numbers on small microcontrollers and keep in mind the limits of each variable type, along with what the default data type for literals is.

  • char: -128 to +127
  • unsigned char / byte: 0 to 255
  • int: -32,768 to +32,767
  • unsigned int: 0 to 65,535
  • long: -2,147,483,648 to 2,147,483,647
  • unsigned long: 0 to 4,294,967,295

You can force literals to be a specific data type by adding a suffix to them:

  • L: Long
  • UL: Unsigned long

(There are others too but less often used).

So your first (failed) calculation can be fixed by forcing one of the values to be a long:

  Serial.println(feedLimit*60*1000L);

1: It seems avr-gcc isn't clever enough to do this, instead it's always treating each operation separately, and each time it works from int-to-int using two 8-bit registers to represent each value or result.

Source Link
Majenko
  • 105.9k
  • 5
  • 82
  • 139

This is not a bug. It's just that you are working with signed 16 bit integers on an 8 bit microcontroller. The maximum an int can store is 32767, and literals are 16 bit signed by default.

Your three prints are:

  Serial.println(feedLimit);

Print 30.

  Serial.println(feedLimit*60*1000);

Multiply feedLimit by 60 = 1800. Multuply 1800 by 1000 = 1800000. Fit 1800000 into 16 bit signed = 30538. Also works the other way: preprocessor multiplies two 16 bit ints to create a new 16 bit int constant: 60 * 1000 = 60000, which is too big to fit in an int and truncates to 27232. Multiply by 30 = 30538 when truncated to 16 bits.

  Serial.println(feedLimit*60000);

Multiply 30 by 60000. 60000 is too big to store in an int so it is implicitly promoted to a long. 30 * 60000 = 1800000. The result is a long because you have an implicit long in your calculation.

You have to take care with big numbers on small microcontrollers and keep in mind the limits of each variable type, along with what the default data type for literals is.

  • char: -128 to +127
  • unsigned char / byte: 0 to 255
  • int: -32,768 to +32,767
  • unsigned int: 0 to 65,535
  • long: -2,147,483,648 to 2,147,483,647
  • unsigned long: 0 to 4,294,967,295

You can force literals to be a specific data type by adding a suffix to them:

  • L: Long
  • UL: Unsigned long

(There are others too but less often used).

So your first (failed) calculation can be fixed by forcing one of the values to be a long:

  Serial.println(feedLimit*60*1000L);