Related: tdf#44076 do not leave cast to int to undefined behaviour
... if the double is an out-of-int-range value.
Also catch domain and pole and range errors.
Move this to it's own sc::power() function that can be reused for
example by ScMatrix::PowOp() to be congruent.
Change-Id: I88331e02e6cdfb5e1dcbf81622d3fc7ce4510478
Reviewed-on: https://gerrit.libreoffice.org/65986
Tested-by: Jenkins
Reviewed-by: Eike Rathke <erack@redhat.com>
diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk
index 3ef18fa..9e67772 100644
--- a/sc/Library_sc.mk
+++ b/sc/Library_sc.mk
@@ -251,6 +251,7 @@
sc/source/core/tool/jumpmatrix \
sc/source/core/tool/listenerquery \
sc/source/core/tool/lookupcache \
sc/source/core/tool/math \
sc/source/core/tool/matrixoperators \
sc/source/core/tool/navicfg \
sc/source/core/tool/numformat \
diff --git a/sc/inc/math.hxx b/sc/inc/math.hxx
index c2a3b99..b4a6476 100644
--- a/sc/inc/math.hxx
+++ b/sc/inc/math.hxx
@@ -21,6 +21,7 @@
#define INCLUDED_SC_INC_MATH_HXX
#include <formula/errorcodes.hxx>
#include <rtl/math.hxx>
namespace sc {
@@ -65,6 +66,13 @@
return fNumerator / fDenominator;
}
/** Return pow(fVal1,fVal2) with error handling.
If an error was detectect, a coded double error of
FormulaError::IllegalFPOperation is returned.
*/
double power( const double& fVal1, const double& fVal2 );
}
#endif
diff --git a/sc/source/core/tool/interpr5.cxx b/sc/source/core/tool/interpr5.cxx
index a819771..aef35f1 100644
--- a/sc/source/core/tool/interpr5.cxx
+++ b/sc/source/core/tool/interpr5.cxx
@@ -1593,18 +1593,7 @@
}
else
{
if (fVal1 < 0 && fVal2 != 0.0)
{
int i = static_cast<int>(1 / fVal2 + ((fVal2 < 0) ? -0.5 : 0.5));
if (i % 2 != 0 && rtl::math::approxEqual(1 / static_cast<double>(i), fVal2))
PushDouble(-pow(-fVal1, fVal2));
else
PushDouble(pow(fVal1, fVal2));
}
else
{
PushDouble(pow(fVal1,fVal2));
}
PushDouble( sc::power( fVal1, fVal2));
}
}
diff --git a/sc/source/core/tool/math.cxx b/sc/source/core/tool/math.cxx
new file mode 100644
index 0000000..73fbfcc
--- /dev/null
+++ b/sc/source/core/tool/math.cxx
@@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* 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/.
*/
#include <math.hxx>
#include <cmath>
#include <cerrno>
#include <cfenv>
namespace sc {
static double err_pow( const double& fVal1, const double& fVal2 )
{
// pow() is expected to set domain error or pole error or range error (or
// flag them via exceptions) or return NaN or Inf.
assert((math_errhandling & (MATH_ERRNO | MATH_ERREXCEPT)) != 0);
std::feclearexcept(FE_ALL_EXCEPT);
errno = 0;
return pow( fVal1, fVal2);
}
double power( const double& fVal1, const double& fVal2 )
{
double fPow;
if (fVal1 < 0 && fVal2 != 0.0)
{
const double f = 1.0 / fVal2 + ((fVal2 < 0.0) ? -0.5 : 0.5);
if (f < SAL_MIN_INT64 || f > SAL_MAX_INT64)
{
// Casting to int would be undefined behaviour.
fPow = err_pow( fVal1, fVal2);
}
else
{
const sal_Int64 i = static_cast<sal_Int64>(f);
if (i % 2 != 0 && rtl::math::approxEqual(1 / static_cast<double>(i), fVal2))
fPow = -err_pow( -fVal1, fVal2);
else
fPow = err_pow( fVal1, fVal2);
}
}
else
{
fPow = err_pow( fVal1, fVal2);
}
// The pow() call must had been the most recent call to check errno or exception.
if ((((math_errhandling & MATH_ERRNO) != 0) && (errno == EDOM || errno == ERANGE))
|| (((math_errhandling & MATH_ERREXCEPT) != 0)
&& std::fetestexcept( FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW))
|| !rtl::math::isFinite(fPow))
{
fPow = CreateDoubleError( FormulaError::IllegalFPOperation);
}
return fPow;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/solenv/clang-format/blacklist b/solenv/clang-format/blacklist
index cb825b7..c86246f 100644
--- a/solenv/clang-format/blacklist
+++ b/solenv/clang-format/blacklist
@@ -10399,6 +10399,7 @@
sc/source/core/tool/jumpmatrix.cxx
sc/source/core/tool/listenerquery.cxx
sc/source/core/tool/lookupcache.cxx
sc/source/core/tool/math.cxx
sc/source/core/tool/matrixoperators.cxx
sc/source/core/tool/navicfg.cxx
sc/source/core/tool/numformat.cxx