[Solved] Problem with overflowing integer

hi,
I have an issue when using this formula:

weight_vector_length = sqrt((acc_x*acc_x)+(acc_y*acc_y)*(acc_z*acc_z));
Serial.print(weight_vector_length);

Every named variable here is 16bits integer (by default).
The goal was to retrieve the length of the weight vector exerced on my accelerometer.

The result of this should always be near 16384, as it is corresponding to 1g for my accelerometer.
My issue is that it, no matter what, gives me a weird small number, like 177.
When I tried to put "weight_vector_length" as a 32bit integer, it would always give me the inferior limit : ‚ąí2147483648.

I did the math and I should get a result of 16909 as my acc_x, acc_y and acc_z were respectively of 14708, -172 and 8340.

I think it's called an overflow but I have no idea what is causing it (as none of the values exceed 32767), I'm not really sure what to do :confused:

1 Like

Even if none of the individual values are larger than 32767, the results of some of the intermediate calculations might be. Since both operands are 16bit, the math will be done with 16bit. So the piece in one sat of parens or another might be overflowing.

The answer is to cast all of those calculations to 32 bit.

weight_vector_length = sqrt(((long)acc_x*acc_x)+((long)acc_y*acc_y)*((long)acc_z*acc_z));
Serial.print(weight_vector_length);

I used long instead of unsigned long because it isn't clear if those values need to be signed or not. Change as you see fit.

1 Like

In other words try this:

weight_vector_length = sqrt(((uint32_t)acc_x*acc_x)+((uint32_t)acc_y*acc_y)*((uint32_t)acc_z*acc_z));

boylesg:
In other words try this:

weight_vector_length = sqrt(((uint32_t)acc_x*acc_x)+((uint32_t)acc_y*acc_y)*((uint32_t)acc_z*acc_z));

But don't do this if your values are signed.

The result of this should always be near 16384, as it is corresponding to 1g for my accelerometer

And any individual axial measurement certainly can be negative, if that axis is pointing down.

Also change

sqrt((...) + (...) * (...))

to

sqrt((...) + (...) + (...))

You could use a function:

inline long squared(long x) {return x * x;}

The compiler will do an implicit conversion to long.

  // sqrt() returns a float so round to the nearest int.
  weight_vector_length = 0.5 + sqrt(squared(acc_x) + squared(acc_y) + squared(acc_z));
int acc_x = 14708;  //0x3974*0x3974 = 0x0CE4DC90 = 32-bit = 216325264
int acc_y = -172;   //-172*-172 = 0xFF54*0xFF54 = 0xFEA87390 = 32-bit = 29584
int acc_z = 8340;   //0x2094*0x2094 = 0x04255590 = 32-bit = 69555600
void setup()
{
  Serial.begin(115200);
  long x = (long)acc_x * (long)acc_x; //why 32-bit processing buffer size for each through long cast?
  long y = (long)acc_y * (long)acc_y;
  long z = (long)acc_z * (long)acc_z;

  Serial.print(x, HEX); //CE4DC90
  Serial.print(" ");
  Serial.println(x, DEC);//216325264

  Serial.print(y, HEX); //7390
  Serial.print(" ");
  Serial.println(y, DEC);//29584

  Serial.print(z, HEX); //4255590
  Serial.print(" ");
  Serial.println(z, DEC);//69555600

  long sum = x + y + z;
  float weight_vector_length = (float)sqrt(sum);

  Serial.println(weight_vector_length, 2); //shows: 16908.89
  Serial.println("====================================================");
  weight_vector_length = (float)sqrt(((long)acc_x * acc_x) + ((long)acc_y * acc_y) + ((long)acc_z * acc_z));
  Serial.println(weight_vector_length, 2); //shows: 16908.89
  Serial.println("====================================================");
  weight_vector_length = (float)sqrt(((long)acc_x * (long)acc_x) + ((long)acc_y * (long)acc_y) + ((long)acc_z * (long)acc_z));
  Serial.println(weight_vector_length, 2); //shows: 16908.89
}

void loop()
{

}

sqrootX1.png

sqrootX1.png

johnwasser:
You could use a function:

inline long squared(long x) {return x * x;}

The compiler will do an implicit conversion to long.

  // sqrt() returns a float so round to the nearest int.

weight_vector_length = 0.5 + sqrt(squared(acc_x) + squared(acc_y) + squared(acc_z));

The output is 'nan' when it should be 16908.89?

int acc_x = 14708;  //0x3974*0x3974 = 0x0CE4DC90 = 32-bit = 216325264
int acc_y = -172;   //-172*-172 = 0xFF54*0xFF54 = 0xFEA87390 = 32-bit = 29584
int acc_z = 8340;   //0x2094*0x2094 = 0x04255590 = 32-bit = 69555600

void setup()
{
  Serial.begin(115200);
   // sqrt() returns a float so round to the nearest int.
  float weight_vector_length = (float)(0.5 + sqrt(sq(acc_x) + sq(acc_y) + sq(acc_z)));
  Serial.println(weight_vector_length, 2);
}

void loop()
{

}

GolamMostafa:
The output is 'nan' when it should be 16908.89?

Because you used the macro sq() instead of defining a function.

/Arduino.h:99:17: definition of macro 'sq'
#define sq(x) ((x)*(x))

Delta_G:
Even if none of the individual values are larger than 32767, the results of some of the intermediate calculations might be. Since both operands are 16bit, the math will be done with 16bit. So the piece in one sat of parens or another might be overflowing.

The answer is to cast all of those calculations to 32 bit.

weight_vector_length = sqrt(((long)acc_x*acc_x)+((long)acc_y*acc_y)*((long)acc_z*acc_z));

Serial.print(weight_vector_length);




I used long instead of unsigned long because it isn't clear if those values need to be signed or not. Change as you see fit.

That makes sense, the intermediate calculation does reach over before the square root.
That solution worked perfectly, it now gives me the correct answer, thanks :slight_smile: !

oqibidipo:
Also change

sqrt((...) + (...) * (...))

to

sqrt((...) + (...) + (...))

Oops made a typo, I didn't copy paste it

johnwasser:
Because you used the macro sq() instead of defining a function.

/Arduino.h:99:17: definition of macro 'sq'

#define sq(x) ((x)*(x))

Any example codes from your side?

QuantumJump:
That makes sense, the intermediate calculation does reach over before the square root.
That solution worked perfectly, it now gives me the correct answer, thanks :slight_smile: !

Please, post the codes that you executed to get correct answer.

GolamMostafa:
Any example codes from your side?

See reply #6.

johnwasser:
See reply #6.

Thanks(+); it works; but --

0.5 is to be removed to get the correct answer of: 16908.89.

With 0.5 in place, the result is: 16909.39.

Can you please, explain the role of the keyword inline. At the absence of this keyword, the result is nan.

int acc_x = 14708;  //0x3974*0x3974 = 0x0CE4DC90 = 32-bit = 216325264
int acc_y = -172;   //-172*-172 = 0xFF54*0xFF54 = 0xFEA87390 = 32-bit = 29584
int acc_z = 8340;   //0x2094*0x2094 = 0x04255590 = 32-bit = 69555600

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

  // sqrt() returns a float so round to the nearest int.
  float weight_vector_length =  0.5 + sqrt(squared(acc_x) + squared(acc_y) + squared(acc_z));
  Serial.println(weight_vector_length, 2);
}

void loop() 
{
  
}

inline long squared(long x)
{
  return x * x;
}

GolamMostafa:
0.5 is to be removed to get the correct answer of: 16908.89.
With 0.5 in place, the result is: 16909.39.

The OP said that all the variable were 'int' so the results are truncated. Adding 0.5 rounds the result to the nearest integer: 16909 with an error of 0.39 instead of truncating to 16908 with an error of 0.89.
In your version of the sketch the 'inline' keyword does nothing. Present or not the code takes 2602 bytes and displays the answer 16909.39. The 'inline' keyword is a hint to the compiler that it may be worthwhile to treat the function more as a macro. It's useful for very small functions where the function call overhead would be a significant portion of the code. In my test code where I was testing on char, byte, int, unsigned, long, and unsigned long there were many more calls to squared() and adding the 'inline' keyword made the code significantly shorter.

johnwasser:
The OP said that all the variable were 'int' so the results are truncated. Adding 0.5 rounds the result to the nearest integer: 16909 with an error of 0.39 instead of truncating to 16908 with an error of 0.89.
In your version of the sketch the 'inline' keyword does nothing. Present or not the code takes 2602 bytes and displays the answer 16909.39. The 'inline' keyword is a hint to the compiler that it may be worthwhile to treat the function more as a macro. It's useful for very small functions where the function call overhead would be a significant portion of the code. In my test code where I was testing on char, byte, int, unsigned, long, and unsigned long there were many more calls to squared() and adding the 'inline' keyword made the code significantly shorter.

Cool!

Please, post the codes that you executed to get correct answer.

The same as in Delta_G reply :

weight_vector_length = sqrt(((long)acc_x*acc_x)+((long)acc_y*acc_y)+((long)acc_z*acc_z));