detect add() bug (#4)
This commit is contained in:
parent
5cbc9c0651
commit
a751d44368
6 changed files with 134 additions and 66 deletions
|
@ -21,7 +21,8 @@ The stability of the formulas is improved by the help of Gil Ross (Thanks!)
|
||||||
- **Statistic(bool useStdDev = true)** Constructor, default use the standard deviation
|
- **Statistic(bool useStdDev = true)** Constructor, default use the standard deviation
|
||||||
functions. Setting this flag to **false** reduces math so slight increase of performance.
|
functions. Setting this flag to **false** reduces math so slight increase of performance.
|
||||||
- **void clear(bool useStdDev = true)** resets all variables.
|
- **void clear(bool useStdDev = true)** resets all variables.
|
||||||
- **void add(float value)**
|
- **float add(float value)** (since 0.4.3) returns value actually added to internal sum.
|
||||||
|
If this is (much) different from what should be added it becomes time to call **clear()**
|
||||||
- **uint32_t count()** returns zero if count == zero (of course)
|
- **uint32_t count()** returns zero if count == zero (of course)
|
||||||
- **float sum()** returns zero if count == zero
|
- **float sum()** returns zero if count == zero
|
||||||
- **float minimum()** returns zero if count == zero
|
- **float minimum()** returns zero if count == zero
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// FILE: Statistic.cpp
|
// FILE: Statistic.cpp
|
||||||
// AUTHOR: Rob dot Tillaart at gmail dot com
|
// AUTHOR: Rob dot Tillaart at gmail dot com
|
||||||
// modified at 0.3 by Gil Ross at physics dot org
|
// modified at 0.3 by Gil Ross at physics dot org
|
||||||
// VERSION: 0.4.2
|
// VERSION: 0.4.3
|
||||||
// PURPOSE: Recursive statistical library for Arduino
|
// PURPOSE: Recursive statistical library for Arduino
|
||||||
//
|
//
|
||||||
// NOTE: 2011-01-07 Gill Ross
|
// NOTE: 2011-01-07 Gill Ross
|
||||||
|
@ -54,6 +54,7 @@
|
||||||
// Added flag to switch on the use of stdDev runtime. [idea marc.recksiedl]
|
// Added flag to switch on the use of stdDev runtime. [idea marc.recksiedl]
|
||||||
// 0.4.1 2020-06-19 fix library.json
|
// 0.4.1 2020-06-19 fix library.json
|
||||||
// 0.4.2 2021-01-08 add Arduino-CI + unit tests
|
// 0.4.2 2021-01-08 add Arduino-CI + unit tests
|
||||||
|
// 0.4.3 2021-01-20 add() returns how much was actually added.
|
||||||
|
|
||||||
|
|
||||||
#include "Statistic.h"
|
#include "Statistic.h"
|
||||||
|
@ -67,51 +68,53 @@ Statistic::Statistic(bool useStdDev)
|
||||||
|
|
||||||
void Statistic::clear(bool useStdDev) // useStdDev default true.
|
void Statistic::clear(bool useStdDev) // useStdDev default true.
|
||||||
{
|
{
|
||||||
_cnt = 0;
|
_cnt = 0;
|
||||||
_sum = 0;
|
_sum = 0;
|
||||||
_min = 0;
|
_min = 0;
|
||||||
_max = 0;
|
_max = 0;
|
||||||
_useStdDev = useStdDev;
|
_useStdDev = useStdDev;
|
||||||
_ssqdif = 0.0;
|
_ssqdif = 0.0;
|
||||||
// note not _ssq but sum of square differences
|
// note not _ssq but sum of square differences
|
||||||
// which is SUM(from i = 1 to N) of f(i)-_ave_N)**2
|
// which is SUM(from i = 1 to N) of f(i)-_ave_N)**2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// adds a new value to the data-set
|
// adds a new value to the data-set
|
||||||
void Statistic::add(const float value)
|
float Statistic::add(const float value)
|
||||||
{
|
{
|
||||||
if (_cnt == 0)
|
float previousSum = _sum;
|
||||||
{
|
if (_cnt == 0)
|
||||||
_min = value;
|
{
|
||||||
_max = value;
|
_min = value;
|
||||||
} else {
|
_max = value;
|
||||||
if (value < _min) _min = value;
|
} else {
|
||||||
else if (value > _max) _max = value;
|
if (value < _min) _min = value;
|
||||||
}
|
else if (value > _max) _max = value;
|
||||||
_sum += value;
|
}
|
||||||
_cnt++;
|
_sum += value;
|
||||||
|
_cnt++;
|
||||||
|
|
||||||
if (_useStdDev && (_cnt > 1))
|
if (_useStdDev && (_cnt > 1))
|
||||||
{
|
{
|
||||||
float _store = (_sum / _cnt - value);
|
float _store = (_sum / _cnt - value);
|
||||||
_ssqdif = _ssqdif + _cnt * _store * _store / (_cnt - 1);
|
_ssqdif = _ssqdif + _cnt * _store * _store / (_cnt - 1);
|
||||||
|
|
||||||
// ~10% faster but limits the amount of samples to 65K as _cnt*_cnt overflows
|
// ~10% faster but limits the amount of samples to 65K as _cnt*_cnt overflows
|
||||||
// float _store = _sum - _cnt * value;
|
// float _store = _sum - _cnt * value;
|
||||||
// _ssqdif = _ssqdif + _store * _store / (_cnt*_cnt - _cnt);
|
// _ssqdif = _ssqdif + _store * _store / (_cnt*_cnt - _cnt);
|
||||||
//
|
//
|
||||||
// solution: TODO verify
|
// solution: TODO verify
|
||||||
// _ssqdif = _ssqdif + (_store * _store / _cnt) / (_cnt - 1);
|
// _ssqdif = _ssqdif + (_store * _store / _cnt) / (_cnt - 1);
|
||||||
}
|
}
|
||||||
|
return _sum - previousSum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// returns the average of the data-set added sofar
|
// returns the average of the data-set added sofar
|
||||||
float Statistic::average() const
|
float Statistic::average() const
|
||||||
{
|
{
|
||||||
if (_cnt == 0) return NAN; // prevent DIV0 error
|
if (_cnt == 0) return NAN; // prevent DIV0 error
|
||||||
return _sum / _cnt;
|
return _sum / _cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -119,25 +122,25 @@ float Statistic::average() const
|
||||||
// http://www.suite101.com/content/how-is-standard-deviation-used-a99084
|
// http://www.suite101.com/content/how-is-standard-deviation-used-a99084
|
||||||
float Statistic::variance() const
|
float Statistic::variance() const
|
||||||
{
|
{
|
||||||
if (!_useStdDev) return NAN;
|
if (!_useStdDev) return NAN;
|
||||||
if (_cnt == 0) return NAN; // prevent DIV0 error
|
if (_cnt == 0) return NAN; // prevent DIV0 error
|
||||||
return _ssqdif / _cnt;
|
return _ssqdif / _cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float Statistic::pop_stdev() const
|
float Statistic::pop_stdev() const
|
||||||
{
|
{
|
||||||
if (!_useStdDev) return NAN;
|
if (!_useStdDev) return NAN;
|
||||||
if (_cnt == 0) return NAN; // prevent DIV0 error
|
if (_cnt == 0) return NAN; // prevent DIV0 error
|
||||||
return sqrt( _ssqdif / _cnt);
|
return sqrt( _ssqdif / _cnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float Statistic::unbiased_stdev() const
|
float Statistic::unbiased_stdev() const
|
||||||
{
|
{
|
||||||
if (!_useStdDev) return NAN;
|
if (!_useStdDev) return NAN;
|
||||||
if (_cnt < 2) return NAN; // prevent DIV0 error
|
if (_cnt < 2) return NAN; // prevent DIV0 error
|
||||||
return sqrt( _ssqdif / (_cnt - 1));
|
return sqrt( _ssqdif / (_cnt - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- END OF FILE --
|
// -- END OF FILE --
|
||||||
|
|
45
Statistic.h
45
Statistic.h
|
@ -3,7 +3,7 @@
|
||||||
// FILE: Statistic.h
|
// FILE: Statistic.h
|
||||||
// AUTHOR: Rob dot Tillaart at gmail dot com
|
// AUTHOR: Rob dot Tillaart at gmail dot com
|
||||||
// modified at 0.3 by Gil Ross at physics dot org
|
// modified at 0.3 by Gil Ross at physics dot org
|
||||||
// VERSION: 0.4.2
|
// VERSION: 0.4.3
|
||||||
// PURPOSE: Recursive Statistical library for Arduino
|
// PURPOSE: Recursive Statistical library for Arduino
|
||||||
// HISTORY: See Statistic.cpp
|
// HISTORY: See Statistic.cpp
|
||||||
//
|
//
|
||||||
|
@ -13,34 +13,37 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
|
||||||
#define STATISTIC_LIB_VERSION (F("0.4.2"))
|
#define STATISTIC_LIB_VERSION (F("0.4.3"))
|
||||||
|
|
||||||
class Statistic
|
class Statistic
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Statistic(bool useStdDev = true); // "switches on/off" stdev run time
|
Statistic(bool useStdDev = true); // "switches on/off" stdev run time
|
||||||
void clear(bool useStdDev = true); // "switches on/off" stdev run time
|
void clear(bool useStdDev = true); // "switches on/off" stdev run time
|
||||||
void add(const float);
|
float add(const float); // returns value actually added
|
||||||
|
|
||||||
// returns the number of values added
|
|
||||||
uint32_t count() const { return _cnt; }; // zero if count == zero
|
|
||||||
float sum() const { return _sum; }; // zero if count == zero
|
|
||||||
float minimum() const { return _min; }; // zero if count == zero
|
|
||||||
float maximum() const { return _max; }; // zero if count == zero
|
|
||||||
float average() const; // NAN if count == zero
|
|
||||||
|
|
||||||
// useStdDev must be true to use next three
|
// returns the number of values added
|
||||||
float variance() const; // NAN if count == zero
|
uint32_t count() const { return _cnt; }; // zero if count == zero
|
||||||
float pop_stdev() const; // population stdev // NAN if count == zero
|
float sum() const { return _sum; }; // zero if count == zero
|
||||||
float unbiased_stdev() const; // NAN if count == zero
|
float minimum() const { return _min; }; // zero if count == zero
|
||||||
|
float maximum() const { return _max; }; // zero if count == zero
|
||||||
|
float average() const; // NAN if count == zero
|
||||||
|
|
||||||
|
|
||||||
|
// useStdDev must be true to use next three
|
||||||
|
float variance() const; // NAN if count == zero
|
||||||
|
float pop_stdev() const; // population stdev // NAN if count == zero
|
||||||
|
float unbiased_stdev() const; // NAN if count == zero
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t _cnt;
|
uint32_t _cnt;
|
||||||
float _sum;
|
float _sum;
|
||||||
float _min;
|
float _min;
|
||||||
float _max;
|
float _max;
|
||||||
bool _useStdDev;
|
bool _useStdDev;
|
||||||
float _ssqdif; // sum of squares difference
|
float _ssqdif; // sum of squares difference
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
61
examples/statistic_add_overflow/statistic_add_overflow.ino
Normal file
61
examples/statistic_add_overflow/statistic_add_overflow.ino
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
//
|
||||||
|
// FILE: TimingTest.ino
|
||||||
|
// AUTHOR: Rob dot Tillaart at gmail dot com
|
||||||
|
// VERSION: 0.2.0
|
||||||
|
// PURPOSE: this sketch shows a known problem when
|
||||||
|
// internal sum is orders of magnitude larger
|
||||||
|
// than the added value.
|
||||||
|
|
||||||
|
#include "Statistic.h"
|
||||||
|
|
||||||
|
Statistic myStats;
|
||||||
|
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println(__FILE__);
|
||||||
|
Serial.print("STATISTIC_LIB_VERSION: ");
|
||||||
|
Serial.println(STATISTIC_LIB_VERSION);
|
||||||
|
|
||||||
|
myStats.clear();
|
||||||
|
|
||||||
|
Serial.println("\nCOUNT\tVALUE\tACTUAL\tRATIO");
|
||||||
|
for (float value = 1e8; value > 1; value *= 0.1)
|
||||||
|
{
|
||||||
|
float actual = myStats.add(value);
|
||||||
|
float ratio = actual / value;
|
||||||
|
|
||||||
|
Serial.print(myStats.count());
|
||||||
|
Serial.print('\t');
|
||||||
|
Serial.print(value);
|
||||||
|
Serial.print('\t');
|
||||||
|
Serial.print(actual);
|
||||||
|
Serial.print('\t');
|
||||||
|
Serial.print(ratio);
|
||||||
|
Serial.print('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (float value = 10; value > 0.1; value -= 1)
|
||||||
|
{
|
||||||
|
float actual = myStats.add(value);
|
||||||
|
float ratio = actual / value;
|
||||||
|
|
||||||
|
Serial.print(myStats.count());
|
||||||
|
Serial.print('\t');
|
||||||
|
Serial.print(value);
|
||||||
|
Serial.print('\t');
|
||||||
|
Serial.print(actual);
|
||||||
|
Serial.print('\t');
|
||||||
|
Serial.print(ratio);
|
||||||
|
Serial.print('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("\nQED...");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- END OF FILE --
|
|
@ -18,7 +18,7 @@
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/RobTillaart/Statistic.git"
|
"url": "https://github.com/RobTillaart/Statistic.git"
|
||||||
},
|
},
|
||||||
"version":"0.4.2",
|
"version":"0.4.3",
|
||||||
"frameworks": "arduino",
|
"frameworks": "arduino",
|
||||||
"platforms": "*"
|
"platforms": "*"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name=Statistic
|
name=Statistic
|
||||||
version=0.4.2
|
version=0.4.3
|
||||||
author=Rob Tillaart <rob.tillaart@gmail.com>
|
author=Rob Tillaart <rob.tillaart@gmail.com>
|
||||||
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
|
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
|
||||||
sentence=Library with basic statistical functions for Arduino.
|
sentence=Library with basic statistical functions for Arduino.
|
||||||
|
|
Loading…
Reference in a new issue