Go Down

Topic: square of small int displays as negative number?? (Read 100 times) previous topic - next topic

paynterf

I have a small program to calculate the incremental variance of a series of small (less than 255) integers acquired from a LIDAR unit.  The calculation involves squaring the distance values and then dividing them by the number of distances considered.  This works fine, except occasionally I get a negative number reported by Serial.println() for dist * dist.  This obviously cannot be true, but I cannot figure out what I'm doing wrong.  I've included the code below, along with a sample printout.  I've seen both the 'olddist_squared' and 'newdist_squared' values reported as negative numbers.

NOTE:  I had to include the printout as an attachment to get under the 9000 character limit.  In the attachment, search for 'olddist_squared = -26332.00', 'olddist_squared = -29436.00' and 'newdist_squared = -30567.00'


TIA,

Frank

Code: [Select]

/*
    Name:       PrintExTest.ino
    Created: 11/5/2018 8:03:18 AM
    Author:     FRANKWIN10\Frank
*/
#include <elapsedMillis/elapsedMillis.h>
#include <PrintEx.h>
StreamEx mySerial = Serial;
const int DIST_ARRAY_SIZE = 5;
byte aFrontDist[DIST_ARRAY_SIZE];
int bitbucket_dist = 0; //the oldest distance
elapsedMillis sinceLastNavUpdateMsec; //added 10/15/18 to replace lastmillisec
double last_incvar = 0;
double last_incmean = 0;
const int MIN_PING_INTERVAL_MSEC = 200; //rev 03/12/16

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

//04/01/15 initialize distance array
for (int i = 0; i < DIST_ARRAY_SIZE; i++)
{
aFrontDist[i] = random(10, 200);
}


//04/01/15 display initial distance array contents
for (int i = 0; i < DIST_ARRAY_SIZE; i++)
{
mySerial.printf("aFrontDist[%d] = %d\n",i, aFrontDist[i]);
}


//calc mean
long sum = 0;
for (int i = 0; i < DIST_ARRAY_SIZE; i++)
{
sum += aFrontDist[i]; //adds in rest of values
}
float brute_mean = (float)sum / (float)DIST_ARRAY_SIZE;
Serial.print("sum = "); Serial.print(sum); Serial.print(", mean = "); Serial.println(brute_mean);


// Step2: Sum up squared deviation of each array item from mean
float sumsquares = 0;
for (int i = 0; i < DIST_ARRAY_SIZE; i++)
{
sumsquares += (aFrontDist[i] - brute_mean)*(aFrontDist[i] - brute_mean);
}

// Step3: Divide squared deviation sum by number of array elements
double brute_var = sumsquares / DIST_ARRAY_SIZE;
Serial.print("sumquares/brute mean/var: "); Serial.print(sumsquares); Serial.print("/");
Serial.print(brute_mean); Serial.print("/"); Serial.println(brute_var);




sinceLastNavUpdateMsec = 0; //added 10/15/18

}

void loop()
{
if (sinceLastNavUpdateMsec >= MIN_PING_INTERVAL_MSEC)
{
sinceLastNavUpdateMsec -= MIN_PING_INTERVAL_MSEC;
int newDistVal = random(0, 200);
CalcDistArrayVariance(newDistVal, aFrontDist);
}



}
double  CalcDistArrayVariance(int newdistval, byte* aDistArray)
{
//Purpose:  Calculate Variance of input array
//Inputs: aDistArray = DIST_ARRAY_SIZE array of integers representing left/right/front distances
//Outputs: Variance of selected array
//Plan:
// Step1: Calculate mean for array
// Step2: Sum up squared deviation of each array item from mean
// Step3: Divide squared deviation sum by number of array elements
//Notes:
// 11/01/18 this function takes about 1.8mSec - small compared to 200mSec loop interval
// 11/02/18 added distval to sig to facilitate incremental calc algorithm

//DEBUG!!
mySerial.printf("CalcDistArrayVariance(%d) called\n", newdistval);

//for (int i = 0; i < DIST_ARRAY_SIZE; i++)
//{
// mySerial.printf("%d\t%d\n", i, aFrontDist[i]);
//}
//DEBUG!!

long funcStartMicrosec = micros();

//11/03/18 update distance array, saving oldest for later use in incremental calcs
//int oldestDistVal = aFrontDist[0];

//calc mean
long sum = 0;
for (int i = 0; i < DIST_ARRAY_SIZE; i++)
{
//aFrontDist[i] = aFrontDist[i + 1];
sum += aDistArray[i]; //adds in rest of values
}
float brute_mean = (float)sum / (float)DIST_ARRAY_SIZE;
Serial.print("sum = "); Serial.print(sum); Serial.print(", mean = "); Serial.println(brute_mean);


// Step2: Sum up squared deviation of each array item from mean
float sumsquares = 0;
for (int i = 0; i < DIST_ARRAY_SIZE; i++)
{
sumsquares += (aDistArray[i] - brute_mean)*(aDistArray[i] - brute_mean);
}

// Step3: Divide squared deviation sum by number of array elements
double brute_var = sumsquares / DIST_ARRAY_SIZE;
Serial.print("sumquares/brute mean/var: "); Serial.print(sumsquares); Serial.print("/");
Serial.print(brute_mean); Serial.print("/"); Serial.println(brute_var);

last_incmean = brute_mean;
last_incvar = brute_var;

//11/02/18 now re-do the calculation using the incremental method, and compare the times
//mu_t = mu_(t-1) - dist_(t-N)/N + dist_t/N
//mu_7 = mu_(6) - dist_(2)/N + dist_7/N

//var^2_t = var^2_(t-1) + dist^2_(t) - dist^2_(t-N) + mu^2_(t-1) - mu^2_t
//var^2_7 = var^2_(6) + dist^2_(7) - dist^2_(t-N) + mu^2_(6) - mu^2_7
//11/03/18 update distance array, saving oldest for later use in incremental calcs
int oldestDistVal = aFrontDist[0];

//shift all frontdist values down one (the 0th value drops into the bit bucket)
//combine this with mean calc
sum = 0;
for (int i = 0; i < DIST_ARRAY_SIZE; i++)
{
aFrontDist[i] = aFrontDist[i + 1];
}
aFrontDist[DIST_ARRAY_SIZE - 1] = newdistval;

for (int i = 0; i < DIST_ARRAY_SIZE; i++)
{
Serial.print("aDistArray["); Serial.print(i); Serial.print("] = "); Serial.println(aDistArray[i]);
}

mySerial.printf("olddist, newdist: %d, %d\n", oldestDistVal, newdistval);
Serial.print("last_incmean, last_incvar "); Serial.print(last_incmean);
Serial.print(", "); Serial.println(last_incvar);
double inc_mean = last_incmean - oldestDistVal / DIST_ARRAY_SIZE + newdistval / DIST_ARRAY_SIZE;

Serial.print("newdist = "); Serial.println(newdistval);
Serial.print("olddist = "); Serial.println(oldestDistVal);
double olddist_squared = oldestDistVal * oldestDistVal;
double newdist_squared = newdistval * newdistval;
Serial.print("olddist_squared = "); Serial.println(olddist_squared);
Serial.print("newdist_squared = "); Serial.println(newdist_squared);

Serial.print("last_inc_var = "); Serial.println(last_incvar);
Serial.print("newdistval*newdistval / DIST_ARRAY_SIZE = "); Serial.println(newdist_squared / DIST_ARRAY_SIZE);
Serial.print("oldestDistVal*oldestDistVal / DIST_ARRAY_SIZE = "); Serial.println(olddist_squared / DIST_ARRAY_SIZE);
Serial.print("last_inc_mean = "); Serial.println(last_incmean);
Serial.print("inc_mean = "); Serial.println(inc_mean);


double inc_var = last_incvar + (newdist_squared / DIST_ARRAY_SIZE) - (olddist_squared / DIST_ARRAY_SIZE)
+ last_incmean * last_incmean - inc_mean * inc_mean;
last_incvar = inc_var; //save for next time
last_incmean = inc_mean; //save for next time

//long inc_varDur = micros() - funcStartMicrosec - brute_varDur;

//display results:
Serial.print("inc_mean/inc_var:"); Serial.print(inc_mean); Serial.print("/");Serial.println(inc_var);

}




jremington

#1
Nov 09, 2018, 04:05 am Last Edit: Nov 09, 2018, 04:07 am by jremington
The largest positive value for a "signed int" (default) variable is 32767, the square root of which is 181.

Overflow tends to result in negative values, so use long int instead.

paynterf

Yeah, that was the problem - I was thinking 65,536, but of course that's for UNSIGNED int.  Sorry to bother y'all ;-)

Regards,

Frank

Go Up