tdf#137679 Use kahan summation for ScInterpreter::CalculateSkew

Change-Id: Ib9e34fd14d9968a5a8c79805da4f12d9a3422de8
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114818
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx
index bdde25a..c7b9379 100644
--- a/sc/source/core/inc/interpre.hxx
+++ b/sc/source/core/inc/interpre.hxx
@@ -835,7 +835,7 @@ private:
    void ScSumX2DY2();
    void ScSumXMY2();
    void ScGrowth();
    bool CalculateSkew(double& fSum,double& fCount,double& vSum,std::vector<double>& values);
    bool CalculateSkew(KahanSum& fSum, double& fCount, std::vector<double>& values);
    void CalculateSkewOrSkewp( bool bSkewp );
    void CalculateSlopeIntercept(bool bSlope);
    void CalculateSmallLarge(bool bSmall);
diff --git a/sc/source/core/tool/interpr3.cxx b/sc/source/core/tool/interpr3.cxx
index 7adee32..d41f4f9 100644
--- a/sc/source/core/tool/interpr3.cxx
+++ b/sc/source/core/tool/interpr3.cxx
@@ -2872,9 +2872,10 @@ void ScInterpreter::ScChiTest()

void ScInterpreter::ScKurt()
{
    double fSum,fCount,vSum;
    KahanSum fSum;
    double fCount;
    std::vector<double> values;
    if ( !CalculateSkew(fSum,fCount,vSum,values) )
    if ( !CalculateSkew(fSum, fCount, values) )
        return;

    // ODF 1.2 constraints: # of numbers >= 4
@@ -2885,31 +2886,30 @@ void ScInterpreter::ScKurt()
        return;
    }

    double fMean = fSum / fCount;

    KahanSum vSum;
    double fMean = fSum.get() / fCount;
    for (double v : values)
        vSum += (v - fMean) * (v - fMean);

    double fStdDev = sqrt(vSum / (fCount - 1.0));
    double xpower4 = 0.0;

    double fStdDev = sqrt(vSum.get() / (fCount - 1.0));
    if (fStdDev == 0.0)
    {
        PushError( FormulaError::DivisionByZero);
        return;
    }

    KahanSum xpower4 = 0.0;
    for (double v : values)
    {
        double dx = (v - fMean) / fStdDev;
        xpower4 = xpower4 + (dx * dx * dx * dx);
        xpower4 += (dx * dx) * (dx * dx);
    }

    double k_d = (fCount - 2.0) * (fCount - 3.0);
    double k_l = fCount * (fCount + 1.0) / ((fCount - 1.0) * k_d);
    double k_t = 3.0 * (fCount - 1.0) * (fCount - 1.0) / k_d;

    PushDouble(xpower4 * k_l - k_t);
    PushDouble(xpower4.get() * k_l - k_t);
}

void ScInterpreter::ScHarMean()
@@ -3220,7 +3220,7 @@ void ScInterpreter::ScStandard()
            PushDouble((x-mue)/sigma);
    }
}
bool ScInterpreter::CalculateSkew(double& fSum,double& fCount,double& vSum,std::vector<double>& values)
bool ScInterpreter::CalculateSkew(KahanSum& fSum, double& fCount, std::vector<double>& values)
{
    short nParamCount = GetByte();
    if ( !MustHaveParamCountMin( nParamCount, 1 )  )
@@ -3228,7 +3228,6 @@ bool ScInterpreter::CalculateSkew(double& fSum,double& fCount,double& vSum,std::

    fSum   = 0.0;
    fCount = 0.0;
    vSum   = 0.0;
    double fVal = 0.0;
    ScAddress aAdr;
    ScRange aRange;
@@ -3328,9 +3327,10 @@ bool ScInterpreter::CalculateSkew(double& fSum,double& fCount,double& vSum,std::

void ScInterpreter::CalculateSkewOrSkewp( bool bSkewp )
{
    double fSum, fCount, vSum;
    KahanSum fSum;
    double fCount;
    std::vector<double> values;
    if (!CalculateSkew( fSum, fCount, vSum, values))
    if (!CalculateSkew( fSum, fCount, values))
        return;
     // SKEW/SKEWP's constraints: they require at least three numbers
    if (fCount < 3.0)
@@ -3340,30 +3340,29 @@ void ScInterpreter::CalculateSkewOrSkewp( bool bSkewp )
        return;
    }

    double fMean = fSum / fCount;

    KahanSum vSum;
    double fMean = fSum.get() / fCount;
    for (double v : values)
        vSum += (v - fMean) * (v - fMean);

    double fStdDev = sqrt( vSum / (bSkewp ? fCount : (fCount - 1.0)));
    double xcube = 0.0;

    double fStdDev = sqrt( vSum.get() / (bSkewp ? fCount : (fCount - 1.0)));
    if (fStdDev == 0)
    {
        PushIllegalArgument();
        return;
    }

    KahanSum xcube = 0.0;
    for (double v : values)
    {
        double dx = (v - fMean) / fStdDev;
        xcube = xcube + (dx * dx * dx);
        xcube += dx * dx * dx;
    }

    if (bSkewp)
        PushDouble( xcube / fCount );
        PushDouble( xcube.get() / fCount );
    else
        PushDouble( ((xcube * fCount) / (fCount - 1.0)) / (fCount - 2.0) );
        PushDouble( ((xcube.get() * fCount) / (fCount - 1.0)) / (fCount - 2.0) );
}

void ScInterpreter::ScSkew()