6

Executing the following code always gives -511 as a result, I performed many tests, and it seems that results are correct from 0 * 0 to 181 * 181, beyond that, results are abnormal, I tried many types for the z variable (int, long, float) without success, any idea? I'm using the Arduino Uno.

long z = 0;

void setup() {

Serial.begin(9600);

}

void loop() {

  z = 255 * 255;
  Serial.println(z);
  delay(200);

} 
Michel Keijzers
  • 12,954
  • 7
  • 40
  • 56
Huskarnov
  • 69
  • 3
  • 2
    Hint: Try "z = 255 * 255L;" or "z = 255 * (long) 255;" – Mikael Patel Feb 05 '19 at 10:25
  • I did.

    @MikaelPatel I worked indeed, could you explain the reason behind this, and is there similar cases for other types?

    – Huskarnov Feb 05 '19 at 10:32
  • 4
    This "bug" is as old as programming. When you write "255" compiler creates an "integer constant" with value of 255. When you multiply 255 by 255 you multiply two "integer constants" and by convention result of such multiplication is an "integer". Then you implicitly cast it to long when doing assignment. When you multiply "255" by "255L" you actually multiply "integer constant" by "long constant" and result of such multiplication is "long". You will see similar issue when you write float z = 1 / 5 result will be 0 because integer division results with integer that is later casted to float. – Filip Franik Feb 05 '19 at 10:54
  • 3
    Ref: https://gcc.gnu.org/wiki/avr-gcc is all the information you need for the AVR GCC compiler. It all has to do with 1) number range of "int" for a given target, and 2) compiler evaluation of constant expressions. – Mikael Patel Feb 05 '19 at 11:03
  • @MikaelPatel You should answer the questions instead of writing snippets of them in the comment section. – pipe Feb 05 '19 at 12:57

2 Answers2

12

No, this is not a bug. You are using a compile time constant expression which is signed int unless told otherwise. Therefore you can only represent numbers between -32768 and 32767. Your computation of 255 * 255 = 65025 exceeds the range. Thus you see an overflow. In the C/C++ standard the overflow of signed types is actually undefined behavior. That means that the compiler is allowed to do anything from showing the correct answer to halt and catch fire. Only unsigned types with a known size like uint16_t have a well-defined overflow behaviour. You should promote your right hand side computation to unsigned type like this:

z = 255U * 255U;

This way you tell the compiler your intention and don't land in "Undefined-Behaviour-Land".

linhartr22
  • 596
  • 4
  • 10
Kwasmich
  • 1,513
  • 12
  • 18
  • @Juraj Right, I thought it was signed 16 bit just as int. But the latter is still true. Compile time constants are signed int, which is 16 bit. So the right side of the assignement is the problem. – Kwasmich Feb 05 '19 at 10:39
  • wrap around doesn't sound right - but I know what you're saying – Jaromanda X Feb 05 '19 at 11:37
0

Assuming long is 4 bytes, it should work.

However, if somehow a long type is only 2 bytes, or (more likely) you used another type or there is some wrong define/typedef, and it is a signed long (default if you do not used the unsigned keyword), the value range is -32,768 to 32,767 and 255 * 255 = 65,025 which is out of range.

So you can first try to use unsigned long.

Juraj
  • 18,037
  • 4
  • 29
  • 49
Michel Keijzers
  • 12,954
  • 7
  • 40
  • 56