Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to "verify" your details, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ Electronics ➜ Microprocessors ➜ Integer arithmetic and overflow

Integer arithmetic and overflow

Postings by administrators only.

Refresh page


Posted by Nick Gammon   Australia  (23,057 posts)  Bio   Forum Administrator
Date Mon 16 Sep 2013 09:34 PM (UTC)

Amended on Tue 23 Jun 2015 11:17 PM (UTC) by Nick Gammon

Message
On the Arduino (Uno) platform, what do you think will be printed here?


void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  
  Serial.println (30000 + 30000); // twice 30000
  Serial.println (60 * 60 * 24);  // seconds in a day
  Serial.println (50 / 100 * 1000); // half of 1000
  }  // end of setup

void loop () { }


Did you guess:


60000
86400
500


Nope!

It prints:


-5536
20864
0


This is because of integer arithmetic. If the compiler can, it treats an numeric literal (like 60) as an int type, which means it has the range -32768 to +32767.

And, arithmetic is done using the type of the largest argument, which means the arithmetic in each case is done as 16-bit arithmetic, and thus it overflows once it reaches 32767.

For example, 30000 + 30000 = 60000 which is 0xEA60 in hex. Unfortunately, 0xEA60 is exactly how -5536 is stored in an int type, which is why it prints -5536.

Meanwhile, 60 * 60 * 24 = 86400 which is 0x15180 in hex. As that doesn't fit in 16 bits, it is truncated to 0x5180 which is 20864 in decimal (as printed).

Finally, in integer arithmetic 50/100 is zero, multiply zero by 1000 and you still get zero, which is why the final result is zero.




So, can we "help" the compiler by telling it the sort of result we want, like this?


void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  long a = 30000 + 30000;
  long b = 60 * 60 * 24;
  float c = 50 / 100 * 1000;
  
  Serial.println (a);
  Serial.println (b);
  Serial.println (c);
  }  // end of setup

void loop () { }


That prints:


-5536
20864
0.00


So no, that hasn't helped.

Solution


First, you can add a suffix to numeric literals (eg. L for long, or UL for unsigned long), and add a decimal place to floats, like this:


void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  
  Serial.println (30000L + 30000); // twice 30000
  Serial.println (60L * 60 * 24);  // seconds in a day
  Serial.println (50.0 / 100 * 1000); // half of 1000
  }  // end of setup

void loop () { }


Now we get:


60000
86400
500.00


You only need to help out with the first literal, once the compiler knows we are using longs (or floats) it sticks with them for the expression.*

Or we can "cast" them:


void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  
  long a = (long) 30000 + 30000;
  long b = (long) 60 * 60 * 24;
  float c = (float) 50 / 100 * 1000;
  
  Serial.println (a);
  Serial.println (b);
  Serial.println (c);
  }  // end of setup

void loop () { }


Results:


60000
86400
500.00


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

An alternative syntax is to use a constructor like this:


void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  
  long a = long (30000) + 30000;
  long b = long (60) * 60 * 24;
  float c = float (50) / 100 * 1000;
  
  Serial.println (a);
  Serial.println (b);
  Serial.println (c);
  }  // end of setup

void loop () { }


(Same results).



* It's actually somewhat more complex than that as this link explains: Understand integer conversion rules

The compiler "promotes" a value in an expression to match another "higher-ranked" type, under certain circumstances. For example, adding an int and a long will result in the int being promoted to a long (regardless of whether it appears first in the expression or not). However if an int is being added to another int, it will not promote them to a long, even though the result may not fit into an int.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


10,643 views.

Postings by administrators only.

Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.