Taking forward the code written for the math library I'm developing. I wrote templatized functions for mean and variance.
std::accumulate()
(or summation in general) is likely to cause overflow. Should I go for an incremental approach?Another point of concern is for templatized argument, I am forced to use the same type as the return type, which means the mean of integers shall also result in an integer, clearly not a good choice. At the same time if I enforce double as the return type, then extending into multi-dimensional types like vectors or complex numbers will be problematic.
Is the comparison for double appropriate? Is there a way to write a generalized comparison function for all float types?
CODE
#include <vector>
#include <numeric>
#include <string>
#include <functional>
namespace Statistics
{
template <typename T>
T average(std::vector<T> distributionVector)
{
if (distributionVector.size() == 0)
{
throw std::invalid_argument("Statistics::average - The distribution provided is empty");
}
return std::accumulate(distributionVector.begin(), distributionVector.end(), T())
/ (distributionVector.size());
}
template <typename T>
T variance(std::vector<T> distributionVector)
{
if (distributionVector.size() == 0)
{
throw std::invalid_argument("Statistics::expectation - The distribution provided is empty");
}
T sumOfSquare = std::accumulate(distributionVector.begin(), distributionVector.end(), T(), [](T a,T b) { return a + b*b; });
T meanOfSquare = sumOfSquare / distributionVector.size();
T squareOfMean = (average(distributionVector)) * (average(distributionVector));
return (meanOfSquare - squareOfMean);
}
}
Test code
#include "pch.h"
#include <vector>
#include "../MathLibrary/Statistics.h"
void compareDoubles(double a, double b)
{
const double THRESHOLD = 0.01;
ASSERT_TRUE(abs(a - b) < THRESHOLD);
}
TEST(Statistics_mean, small_distributions)
{
std::vector<int> testVector = { -2,-1,0,1,2 };
EXPECT_EQ(Statistics::average(testVector), 0);
std::vector<double> testVectorDouble = {5,5,6,6};
compareDoubles(Statistics::average(testVectorDouble), 5.5);
}
TEST(Statistics_mean, empty_distribution)
{
std::vector<int> testVector;
EXPECT_THROW(Statistics::average(testVector), std::invalid_argument);
}
TEST(Statistics_variance, small_distribution)
{
std::vector<double> testVector = { 0,0 };
compareDoubles(Statistics::variance(testVector), 0);
std::vector<double> testVector2 = {1,2,3,4};
compareDoubles(Statistics::variance(testVector2), 1.25);
std::vector<double> testVectorRandom = { 1,2,3,4,6,8,9,34,45,78,89 };
compareDoubles(Statistics::variance(testVectorRandom), 938.2314);
}
throw
for this code? I associatethrow
with code that isn't allowed to crash (gaming/nuclear power plant, that sort of thing). In the case of gaming, the program would likely abandon an image or even a whole frame - this way the player can keep going and put up with a single bad frame. For a nuclear power plant you'd make sure that the controls remain operational and sensible (but perhaps not optimal). Personally I'm a massive fan of hard/early crashes. An assert would do the job here.throw
has hidden efficiency costs too. \$\endgroup\$throw
so the caller can decide whether to crash, back out of that operation, etc. and how to log the error message. \$\endgroup\$throw
is reached. In which case the cost of callingassert
in a catch block compared to a direct call toassert
simply does not matter -- the exception handling cost is miniscule compared to the work of logging and process termination. Only when exceptions are repeatedly thrown and caught may the performance become an issue, and calling assert removes the option entirely. \$\endgroup\$