/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include using namespace formula; double const fHalfMachEps = 0.5 * ::std::numeric_limits::epsilon(); // The idea how this group of gamma functions is calculated, is // based on the Cephes library // online http://www.moshier.net/#Cephes [called 2008-02] /** You must ensure fA>0.0 && fX>0.0 valid results only if fX > fA+1.0 uses continued fraction with odd items */ double ScInterpreter::GetGammaContFraction( double fA, double fX ) { double const fBigInv = ::std::numeric_limits::epsilon(); double const fBig = 1.0/fBigInv; double fCount = 0.0; double fY = 1.0 - fA; double fDenom = fX + 2.0-fA; double fPkm1 = fX + 1.0; double fPkm2 = 1.0; double fQkm1 = fDenom * fX; double fQkm2 = fX; double fApprox = fPkm1/fQkm1; bool bFinished = false; do { fCount = fCount +1.0; fY = fY+ 1.0; const double fNum = fY * fCount; fDenom = fDenom +2.0; double fPk = fPkm1 * fDenom - fPkm2 * fNum; const double fQk = fQkm1 * fDenom - fQkm2 * fNum; if (fQk != 0.0) { const double fR = fPk/fQk; bFinished = (fabs( (fApprox - fR)/fR ) <= fHalfMachEps); fApprox = fR; } fPkm2 = fPkm1; fPkm1 = fPk; fQkm2 = fQkm1; fQkm1 = fQk; if (fabs(fPk) > fBig) { // reduce a fraction does not change the value fPkm2 = fPkm2 * fBigInv; fPkm1 = fPkm1 * fBigInv; fQkm2 = fQkm2 * fBigInv; fQkm1 = fQkm1 * fBigInv; } } while (!bFinished && fCount<10000); // most iterations, if fX==fAlpha+1.0; approx sqrt(fAlpha) iterations then if (!bFinished) { SetError(FormulaError::NoConvergence); } return fApprox; } /** You must ensure fA>0.0 && fX>0.0 valid results only if fX <= fA+1.0 uses power series */ double ScInterpreter::GetGammaSeries( double fA, double fX ) { double fDenomfactor = fA; double fSummand = 1.0/fA; double fSum = fSummand; int nCount=1; do { fDenomfactor = fDenomfactor + 1.0; fSummand = fSummand * fX/fDenomfactor; fSum = fSum + fSummand; nCount = nCount+1; } while ( fSummand/fSum > fHalfMachEps && nCount<=10000); // large amount of iterations will be carried out for huge fAlpha, even // if fX <= fAlpha+1.0 if (nCount>10000) { SetError(FormulaError::NoConvergence); } return fSum; } /** You must ensure fA>0.0 && fX>0.0) */ double ScInterpreter::GetLowRegIGamma( double fA, double fX ) { double fLnFactor = fA * log(fX) - fX - GetLogGamma(fA); double fFactor = exp(fLnFactor); // Do we need more accuracy than exp(ln()) has? if (fX>fA+1.0) // includes fX>1.0; 1-GetUpRegIGamma, continued fraction return 1.0 - fFactor * GetGammaContFraction(fA,fX); else // fX<=1.0 || fX<=fA+1.0, series return fFactor * GetGammaSeries(fA,fX); } /** You must ensure fA>0.0 && fX>0.0) */ double ScInterpreter::GetUpRegIGamma( double fA, double fX ) { double fLnFactor= fA*log(fX)-fX-GetLogGamma(fA); double fFactor = exp(fLnFactor); //Do I need more accuracy than exp(ln()) has?; if (fX>fA+1.0) // includes fX>1.0 return fFactor * GetGammaContFraction(fA,fX); else //fX<=1 || fX<=fA+1, 1-GetLowRegIGamma, series return 1.0 -fFactor * GetGammaSeries(fA,fX); } /** Gamma distribution, probability density function. fLambda is "scale" parameter You must ensure fAlpha>0.0 and fLambda>0.0 */ double ScInterpreter::GetGammaDistPDF( double fX, double fAlpha, double fLambda ) { if (fX < 0.0) return 0.0; // see ODFF else if (fX == 0) // in this case 0^0 isn't zero { if (fAlpha < 1.0) { SetError(FormulaError::DivisionByZero); // should be #DIV/0 return HUGE_VAL; } else if (fAlpha == 1) { return (1.0 / fLambda); } else { return 0.0; } } else { double fXr = fX / fLambda; // use exp(ln()) only for large arguments because of less accuracy if (fXr > 1.0) { const double fLogDblMax = log( ::std::numeric_limits::max()); if (log(fXr) * (fAlpha-1.0) < fLogDblMax && fAlpha < fMaxGammaArgument) { return pow( fXr, fAlpha-1.0) * exp(-fXr) / fLambda / GetGamma(fAlpha); } else { return exp( (fAlpha-1.0) * log(fXr) - fXr - log(fLambda) - GetLogGamma(fAlpha)); } } else // fXr near to zero { if (fAlpha0.0 and fLambda>0.0 */ double ScInterpreter::GetGammaDist( double fX, double fAlpha, double fLambda ) { if (fX <= 0.0) return 0.0; else return GetLowRegIGamma( fAlpha, fX / fLambda); } namespace { class NumericCellAccumulator { KahanSum maSum; FormulaError mnError; public: NumericCellAccumulator() : maSum(0.0), mnError(FormulaError::NONE) {} void operator() (const sc::CellStoreType::value_type& rNode, size_t nOffset, size_t nDataSize) { switch (rNode.type) { case sc::element_type_numeric: { if (nDataSize == 0) return; const double *p = &sc::numeric_block::at(*rNode.data, nOffset); sc::ArraySumFunctor functor(p, nDataSize); maSum += functor(); break; } case sc::element_type_formula: { sc::formula_block::const_iterator it = sc::formula_block::begin(*rNode.data); std::advance(it, nOffset); sc::formula_block::const_iterator itEnd = it; std::advance(itEnd, nDataSize); for (; it != itEnd; ++it) { double fVal = 0.0; FormulaError nErr = FormulaError::NONE; ScFormulaCell& rCell = *(*it); if (!rCell.GetErrorOrValue(nErr, fVal)) // The cell has neither error nor value. Perhaps string result. continue; if (nErr != FormulaError::NONE) { // Cell has error - skip all the rest mnError = nErr; return; } maSum += fVal; } } break; default: ; } } FormulaError getError() const { return mnError; } KahanSum getResult() const { return maSum; } }; class NumericCellCounter { size_t mnCount; public: NumericCellCounter() : mnCount(0) {} void operator() (const sc::CellStoreType::value_type& rNode, size_t nOffset, size_t nDataSize) { switch (rNode.type) { case sc::element_type_numeric: mnCount += nDataSize; break; case sc::element_type_formula: { sc::formula_block::const_iterator it = sc::formula_block::begin(*rNode.data); std::advance(it, nOffset); sc::formula_block::const_iterator itEnd = it; std::advance(itEnd, nDataSize); for (; it != itEnd; ++it) { ScFormulaCell& rCell = **it; if (rCell.IsValueNoError()) ++mnCount; } } break; default: ; } } size_t getCount() const { return mnCount; } }; class FuncCount : public sc::ColumnSpanSet::ColumnAction { const ScInterpreterContext& mrContext; sc::ColumnBlockConstPosition maPos; ScColumn* mpCol; size_t mnCount; sal_uInt32 mnNumFmt; public: FuncCount(const ScInterpreterContext& rContext) : mrContext(rContext), mpCol(nullptr), mnCount(0), mnNumFmt(0) {} virtual void startColumn(ScColumn* pCol) override { mpCol = pCol; mpCol->InitBlockPosition(maPos); } virtual void execute(SCROW nRow1, SCROW nRow2, bool bVal) override { if (!bVal) return; NumericCellCounter aFunc; maPos.miCellPos = sc::ParseBlock(maPos.miCellPos, mpCol->GetCellStore(), aFunc, nRow1, nRow2); mnCount += aFunc.getCount(); mnNumFmt = mpCol->GetNumberFormat(mrContext, nRow2); }; size_t getCount() const { return mnCount; } sal_uInt32 getNumberFormat() const { return mnNumFmt; } }; class FuncSum : public sc::ColumnSpanSet::ColumnAction { const ScInterpreterContext& mrContext; sc::ColumnBlockConstPosition maPos; ScColumn* mpCol; KahanSum mfSum; FormulaError mnError; sal_uInt32 mnNumFmt; public: FuncSum(const ScInterpreterContext& rContext) : mrContext(rContext), mpCol(nullptr), mfSum(0.0), mnError(FormulaError::NONE), mnNumFmt(0) {} virtual void startColumn(ScColumn* pCol) override { mpCol = pCol; mpCol->InitBlockPosition(maPos); } virtual void execute(SCROW nRow1, SCROW nRow2, bool bVal) override { if (!bVal) return; if (mnError != FormulaError::NONE) return; NumericCellAccumulator aFunc; maPos.miCellPos = sc::ParseBlock(maPos.miCellPos, mpCol->GetCellStore(), aFunc, nRow1, nRow2); mnError = aFunc.getError(); if (mnError != FormulaError::NONE) return; mfSum += aFunc.getResult(); mnNumFmt = mpCol->GetNumberFormat(mrContext, nRow2); }; FormulaError getError() const { return mnError; } KahanSum getSum() const { return mfSum; } sal_uInt32 getNumberFormat() const { return mnNumFmt; } }; } static void IterateMatrix( const ScMatrixRef& pMat, ScIterFunc eFunc, bool bTextAsZero, SubtotalFlags nSubTotalFlags, sal_uLong& rCount, SvNumFormatType& rFuncFmtType, KahanSum& fRes ) { if (!pMat) return; const bool bIgnoreErrVal = bool(nSubTotalFlags & SubtotalFlags::IgnoreErrVal); rFuncFmtType = SvNumFormatType::NUMBER; switch (eFunc) { case ifAVERAGE: case ifSUM: { ScMatrix::KahanIterateResult aRes = pMat->Sum(bTextAsZero, bIgnoreErrVal); fRes += aRes.maAccumulator; rCount += aRes.mnCount; } break; case ifCOUNT: rCount += pMat->Count(bTextAsZero, false); // do not count error values break; case ifCOUNT2: /* TODO: what is this supposed to be with bIgnoreErrVal? */ rCount += pMat->Count(true, true); // do count error values break; case ifPRODUCT: { ScMatrix::DoubleIterateResult aRes = pMat->Product(bTextAsZero, bIgnoreErrVal); fRes *= aRes.maAccumulator; rCount += aRes.mnCount; } break; case ifSUMSQ: { ScMatrix::KahanIterateResult aRes = pMat->SumSquare(bTextAsZero, bIgnoreErrVal); fRes += aRes.maAccumulator; rCount += aRes.mnCount; } break; default: ; } } size_t ScInterpreter::GetRefListArrayMaxSize( short nParamCount ) { size_t nSize = 0; if (IsInArrayContext()) { for (short i=1; i <= nParamCount; ++i) { if (GetStackType(i) == svRefList) { const ScRefListToken* p = dynamic_cast(pStack[sp - i]); if (p && p->IsArrayResult() && p->GetRefList()->size() > nSize) nSize = p->GetRefList()->size(); } } } return nSize; } static double lcl_IterResult( ScIterFunc eFunc, double fRes, sal_uLong nCount ) { switch( eFunc ) { case ifAVERAGE: fRes = sc::div( fRes, nCount); break; case ifCOUNT2: case ifCOUNT: fRes = nCount; break; case ifPRODUCT: if ( !nCount ) fRes = 0.0; break; default: ; // nothing } return fRes; } void ScInterpreter::IterateParameters( ScIterFunc eFunc, bool bTextAsZero ) { short nParamCount = GetByte(); const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount); ScMatrixRef xResMat, xResCount; const double ResInitVal = (eFunc == ifPRODUCT) ? 1.0 : 0.0; KahanSum fRes = ResInitVal; double fVal = 0.0; sal_uLong nCount = 0; ScAddress aAdr; ScRange aRange; size_t nRefInList = 0; size_t nRefArrayPos = std::numeric_limits::max(); if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT || ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ) nGlobalError = FormulaError::NONE; while (nParamCount-- > 0) { switch (GetStackType()) { case svString: { if( eFunc == ifCOUNT ) { OUString aStr = PopString().getString(); if ( bTextAsZero ) nCount++; else { // Only check if string can be converted to number, no // error propagation. FormulaError nErr = nGlobalError; nGlobalError = FormulaError::NONE; ConvertStringToValue( aStr ); if (nGlobalError == FormulaError::NONE) ++nCount; nGlobalError = nErr; } } else { Pop(); switch ( eFunc ) { case ifAVERAGE: case ifSUM: case ifSUMSQ: case ifPRODUCT: { if ( bTextAsZero ) { nCount++; if ( eFunc == ifPRODUCT ) fRes = 0.0; } else { while (nParamCount-- > 0) Pop(); SetError( FormulaError::NoValue ); } } break; default: nCount++; } } } break; case svDouble : fVal = GetDouble(); nCount++; switch( eFunc ) { case ifAVERAGE: case ifSUM: fRes += fVal; break; case ifSUMSQ: fRes += fVal * fVal; break; case ifPRODUCT: fRes *= fVal; break; default: ; // nothing } nFuncFmtType = SvNumFormatType::NUMBER; break; case svExternalSingleRef: { ScExternalRefCache::TokenRef pToken; ScExternalRefCache::CellFormat aFmt; PopExternalSingleRef(pToken, &aFmt); if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT || ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ) { nGlobalError = FormulaError::NONE; if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ++nCount; break; } if (!pToken) break; StackVar eType = pToken->GetType(); if (eFunc == ifCOUNT2) { if ( eType != svEmptyCell && ( ( pToken->GetOpCode() != ocSubTotal && pToken->GetOpCode() != ocAggregate ) || ( mnSubTotalFlags & SubtotalFlags::IgnoreNestedStAg ) ) ) nCount++; if (nGlobalError != FormulaError::NONE) nGlobalError = FormulaError::NONE; } else if (eType == svDouble) { nCount++; fVal = pToken->GetDouble(); if (aFmt.mbIsSet) { nFuncFmtType = aFmt.mnType; nFuncFmtIndex = aFmt.mnIndex; } switch( eFunc ) { case ifAVERAGE: case ifSUM: fRes += fVal; break; case ifSUMSQ: fRes += fVal * fVal; break; case ifPRODUCT: fRes *= fVal; break; case ifCOUNT: if ( nGlobalError != FormulaError::NONE ) { nGlobalError = FormulaError::NONE; nCount--; } break; default: ; // nothing } } else if (bTextAsZero && eType == svString) { nCount++; if ( eFunc == ifPRODUCT ) fRes = 0.0; } } break; case svSingleRef : { PopSingleRef( aAdr ); if (nGlobalError == FormulaError::NoRef) { PushError( FormulaError::NoRef); return; } if ( ( ( mnSubTotalFlags & SubtotalFlags::IgnoreFiltered ) && mrDoc.RowFiltered( aAdr.Row(), aAdr.Tab() ) ) || ( ( mnSubTotalFlags & SubtotalFlags::IgnoreHidden ) && mrDoc.RowHidden( aAdr.Row(), aAdr.Tab() ) ) ) { break; } if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT || ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ) { nGlobalError = FormulaError::NONE; if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ++nCount; break; } ScRefCellValue aCell(mrDoc, aAdr); if (!aCell.isEmpty()) { if( eFunc == ifCOUNT2 ) { CellType eCellType = aCell.meType; if ( eCellType != CELLTYPE_NONE ) nCount++; if ( nGlobalError != FormulaError::NONE ) nGlobalError = FormulaError::NONE; } else if (aCell.hasNumeric()) { nCount++; fVal = GetCellValue(aAdr, aCell); CurFmtToFuncFmt(); switch( eFunc ) { case ifAVERAGE: case ifSUM: fRes += fVal; break; case ifSUMSQ: fRes += fVal * fVal; break; case ifPRODUCT: fRes *= fVal; break; case ifCOUNT: if ( nGlobalError != FormulaError::NONE ) { nGlobalError = FormulaError::NONE; nCount--; } break; default: ; // nothing } } else if (bTextAsZero && aCell.hasString()) { nCount++; if ( eFunc == ifPRODUCT ) fRes = 0.0; } } } break; case svRefList : { const ScRefListToken* p = dynamic_cast(pStack[sp-1]); if (p && p->IsArrayResult()) { nRefArrayPos = nRefInList; // The "one value to all references of an array" seems to // be what Excel does if there are other types than just // arrays of references. if (!xResMat) { // Create and init all elements with current value. assert(nMatRows > 0); xResMat = GetNewMat( 1, nMatRows, true); xResMat->FillDouble( fRes.get(), 0,0, 0,nMatRows-1); if (eFunc != ifSUM) { xResCount = GetNewMat( 1, nMatRows, true); xResCount->FillDouble( nCount, 0,0, 0,nMatRows-1); } } else { // Current value and values from vector are operands // for each vector position. if (nCount && xResCount) { for (SCSIZE i=0; i < nMatRows; ++i) { xResCount->PutDouble( xResCount->GetDouble(0,i) + nCount, 0,i); } } if (fRes != ResInitVal) { for (SCSIZE i=0; i < nMatRows; ++i) { double fVecRes = xResMat->GetDouble(0,i); if (eFunc == ifPRODUCT) fVecRes *= fRes.get(); else fVecRes += fRes.get(); xResMat->PutDouble( fVecRes, 0,i); } } } fRes = ResInitVal; nCount = 0; } } [[fallthrough]]; case svDoubleRef : { PopDoubleRef( aRange, nParamCount, nRefInList); if (nGlobalError == FormulaError::NoRef) { PushError( FormulaError::NoRef); return; } if ( nGlobalError != FormulaError::NONE && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT || ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ) { nGlobalError = FormulaError::NONE; if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ++nCount; if ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ) break; } if( eFunc == ifCOUNT2 ) { ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags ); for (bool bHas = aIter.first(); bHas; bHas = aIter.next()) { if ( !aIter.isEmpty() ) { ++nCount; } } if ( nGlobalError != FormulaError::NONE ) nGlobalError = FormulaError::NONE; } else if (((eFunc == ifSUM && !bCalcAsShown) || eFunc == ifCOUNT ) && mnSubTotalFlags == SubtotalFlags::NONE) { // Use fast span set array method. // ifSUM with bCalcAsShown has to use the slow bells and // whistles ScValueIterator below. sc::RangeColumnSpanSet aSet( aRange ); if ( eFunc == ifSUM ) { FuncSum aAction(mrContext); aSet.executeColumnAction( mrDoc, aAction ); FormulaError nErr = aAction.getError(); if ( nErr != FormulaError::NONE ) { PushError( nErr ); return; } fRes += aAction.getSum(); // Get the number format of the last iterated cell. nFuncFmtIndex = aAction.getNumberFormat(); } else { FuncCount aAction(mrContext); aSet.executeColumnAction(mrDoc, aAction); nCount += aAction.getCount(); // Get the number format of the last iterated cell. nFuncFmtIndex = aAction.getNumberFormat(); } nFuncFmtType = mrContext.GetNumberFormatType( nFuncFmtIndex ); } else { ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero ); aValIter.SetInterpreterContext( &mrContext ); FormulaError nErr = FormulaError::NONE; if (aValIter.GetFirst(fVal, nErr)) { // placed the loop on the inside for performance reasons: aValIter.GetCurNumFmtInfo( mrContext, nFuncFmtType, nFuncFmtIndex ); switch( eFunc ) { case ifAVERAGE: case ifSUM: if ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) { do { if ( nErr == FormulaError::NONE ) { SetError(nErr); fRes += fVal; nCount++; } } while (aValIter.GetNext(fVal, nErr)); } else { do { SetError(nErr); fRes += fVal; nCount++; } while (aValIter.GetNext(fVal, nErr)); } break; case ifSUMSQ: if ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) { do { if ( nErr == FormulaError::NONE ) { SetError(nErr); fRes += fVal * fVal; nCount++; } } while (aValIter.GetNext(fVal, nErr)); } else { do { SetError(nErr); fRes += fVal * fVal; nCount++; } while (aValIter.GetNext(fVal, nErr)); } break; case ifPRODUCT: do { if ( !( nErr != FormulaError::NONE && ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) ) { SetError(nErr); fRes *= fVal; nCount++; } } while (aValIter.GetNext(fVal, nErr)); break; case ifCOUNT: do { if ( nErr == FormulaError::NONE ) nCount++; } while (aValIter.GetNext(fVal, nErr)); break; default: ; // nothing } SetError( nErr ); } } if (nRefArrayPos != std::numeric_limits::max()) { // Update vector element with current value. if (xResCount) xResCount->PutDouble( xResCount->GetDouble(0,nRefArrayPos) + nCount, 0,nRefArrayPos); double fVecRes = xResMat->GetDouble(0,nRefArrayPos); if (eFunc == ifPRODUCT) fVecRes *= fRes.get(); else fVecRes += fRes.get(); xResMat->PutDouble( fVecRes, 0,nRefArrayPos); // Reset. fRes = ResInitVal; nCount = 0; nRefArrayPos = std::numeric_limits::max(); } } break; case svExternalDoubleRef: { ScMatrixRef pMat; PopExternalDoubleRef(pMat); if ( nGlobalError != FormulaError::NONE && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) break; IterateMatrix( pMat, eFunc, bTextAsZero, mnSubTotalFlags, nCount, nFuncFmtType, fRes ); } break; case svMatrix : { ScMatrixRef pMat = PopMatrix(); IterateMatrix( pMat, eFunc, bTextAsZero, mnSubTotalFlags, nCount, nFuncFmtType, fRes ); } break; case svError: { PopError(); if ( eFunc == ifCOUNT || ( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) { nGlobalError = FormulaError::NONE; } else if ( eFunc == ifCOUNT2 && !( mnSubTotalFlags & SubtotalFlags::IgnoreErrVal ) ) { nCount++; nGlobalError = FormulaError::NONE; } } break; default : while (nParamCount-- > 0) PopError(); SetError(FormulaError::IllegalParameter); } } // A boolean return type makes no sense on sums et al. // Counts are always numbers. if( nFuncFmtType == SvNumFormatType::LOGICAL || eFunc == ifCOUNT || eFunc == ifCOUNT2 ) nFuncFmtType = SvNumFormatType::NUMBER; if (xResMat) { // Include value of last non-references-array type and calculate final result. for (SCSIZE i=0; i < nMatRows; ++i) { sal_uLong nVecCount = (xResCount ? nCount + xResCount->GetDouble(0,i) : nCount); double fVecRes = xResMat->GetDouble(0,i); if (eFunc == ifPRODUCT) fVecRes *= fRes.get(); else fVecRes += fRes.get(); fVecRes = lcl_IterResult( eFunc, fVecRes, nVecCount); xResMat->PutDouble( fVecRes, 0,i); } PushMatrix( xResMat); } else { PushDouble( lcl_IterResult( eFunc, fRes.get(), nCount)); } } void ScInterpreter::ScSumSQ() { IterateParameters( ifSUMSQ ); } void ScInterpreter::ScSum() { IterateParameters( ifSUM ); } void ScInterpreter::ScProduct() { IterateParameters( ifPRODUCT ); } void ScInterpreter::ScAverage( bool bTextAsZero ) { IterateParameters( ifAVERAGE, bTextAsZero ); } void ScInterpreter::ScCount() { IterateParameters( ifCOUNT ); } void ScInterpreter::ScCount2() { IterateParameters( ifCOUNT2 ); } /** * The purpose of RAWSUBTRACT() is exactly to not apply any error correction, approximation etc. * But use the "raw" IEEE 754 double subtraction. * So no Kahan summation */ void ScInterpreter::ScRawSubtract() { short nParamCount = GetByte(); if (!MustHaveParamCountMin( nParamCount, 2)) return; // Reverse stack to process arguments from left to right. ReverseStack( nParamCount); // Obtain the minuend. double fRes = GetDouble(); while (nGlobalError == FormulaError::NONE && --nParamCount > 0) { // Simple single values without matrix support. fRes -= GetDouble(); } while (nParamCount-- > 0) PopError(); PushDouble( fRes); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */