detect add() bug (#4)

This commit is contained in:
Rob Tillaart 2021-01-20 14:11:45 +01:00 committed by GitHub
parent 5cbc9c0651
commit a751d44368
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 134 additions and 66 deletions

View file

@ -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

View file

@ -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 --

View file

@ -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
}; };

View 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 --

View 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": "*"
} }

View file

@ -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.