Help - I think I broke math!!

:astonished:

Ok, so I'm losing my mind here. Not that unusual for me, but this seems worse than usual.
All of a sudden my Arduinos can't do basic math. It's happening on several boards and with different sketches. I've tried updating my arduino ide from 1.0.2 to 1.0.3... still happening.

Test sketch:

void setup(){
Serial.begin(9600);

}
void loop(){
  unsigned long x=100*1000;
  long y=100*1000;
  int z=100*100;
  int q=500*120;
  Serial.println("Start");
  Serial.println(100000);
  Serial.println(x);
  Serial.println(y);
  Serial.println(z);
  Serial.println(q);
  
  
}

Serial Output reads:

Start
100000
4294936224
-31072
10000
-5536

I don't think I've messed with any of the core arduino functionality (and that should be fixed by using the new version?)
I've rebooted the computer (MacBookPro 2.4 Ghz intel core 2 duo, 4 GB ram, os 10.6.8) multiple times.

Loaded the following code to see if it was strictly a Serial monitor issue, but the LED didn't flash and the "X is under 200000" line didn't flash.

void setup(){
Serial.begin(9600);
pinMode (13,OUTPUT);//for led

}
void loop(){
  unsigned long x=100*1000;
     if (x<200000) {
       digitalWrite (13,HIGH);
      Serial.println("X is under 200000");
     }
     delay(200);
     digitalWrite (13,LOW);
     delay(200);
     
     
  long y=100*1000;
  int z=100*100;
  int q=500*120;
  Serial.println("Start");
  Serial.println(100000);
  Serial.println(x);
  Serial.println(y);
  Serial.println(z);
  Serial.println(q);
 
}

Any thoughts?? Compiler?? Operator error :~
Thanks,
Jon

Ah yes, well I've been working on the below ...


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 overflow. 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).

Thanks Nick!

I've had lots of experience with having to cast floats in similar situations, but it never occurred to me that numeric literals would be compiled as signed ints. Never a day you don't learn something!!

Jon

but it never occurred to me that numeric literals would be compiled as signed ints.

Just out of curiosity, what did you expect?

Actually, the issue is not with the values be interpreted as signed ints. It's with the signed ints being multiplied or divided causing overflow or truncation that you were not expecting/taking into account.

And, that is a CS100 topic that is drilled into students very early.

And, that is a CS100 topic that is drilled into students very early.

As an EE, yes this was mentioned in Digital Circuits and Microprocessors. But to be honest, meant nothing to me until actually using this stuff. So don't feel bad about it. Experience trumps education each and every day. Except when you're applying for a Engineering position. Then the engineering degree will beat out the moderately experienced person (in most cases). Probably more true for doctors.

But to be honest, meant nothing to me until actually using this stuff.

Like most "education".