summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--basic/qa/basic_coverage/test_Currency.bas202
-rw-r--r--basic/source/sbx/sbxbyte.cxx15
-rw-r--r--basic/source/sbx/sbxchar.cxx17
-rw-r--r--basic/source/sbx/sbxconv.hxx75
-rw-r--r--basic/source/sbx/sbxcurr.cxx220
-rw-r--r--basic/source/sbx/sbxint.cxx37
-rw-r--r--basic/source/sbx/sbxlng.cxx12
-rw-r--r--basic/source/sbx/sbxuint.cxx15
-rw-r--r--basic/source/sbx/sbxulng.cxx2
-rw-r--r--basic/source/sbx/sbxvalue.cxx230
-rw-r--r--include/basic/sbxdef.hxx2
-rw-r--r--include/o3tl/safeint.hxx49
12 files changed, 484 insertions, 392 deletions
diff --git a/basic/qa/basic_coverage/test_Currency.bas b/basic/qa/basic_coverage/test_Currency.bas
new file mode 100644
index 000000000000..db6e0bfe03d9
--- /dev/null
+++ b/basic/qa/basic_coverage/test_Currency.bas
@@ -0,0 +1,202 @@
+' 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/.
+'
+
+Option Explicit
+
+Function doUnitTest() As String
+ TestUtil.TestInit
+ verify_testCurrency
+ doUnitTest = TestUtil.GetResult()
+End Function
+
+Sub verify_testCurrency()
+ On Error GoTo errorHandler
+
+ Dim x As Currency, y As Currency
+
+ ' Test division
+
+ verify_DivideByZero(15.1@, 0@)
+ x = 15.1@
+ y = 0.4@
+ TestUtil.AssertEqual(x / y, 37.75@, x & " / " & y)
+ verify_IDivideByZero(x, y) ' y rounds to 0
+ verify_IModByZero(x, y)
+ y = 0.8@
+ TestUtil.AssertEqual(x / y, 18.875@, x & " / " & y)
+ TestUtil.AssertEqual(x \ y, 15, x & " \ " & y) ' y rounds to 1
+ TestUtil.AssertEqual(x Mod y, 0, x & " Mod " & y)
+
+ ' Test various operations; the end result is -922337203685477.5808, i.e. minimal representable value
+ x = 9223372036.8@
+ y = x * 59.000
+ x = x * 59.125
+ x = x - y
+ x = x * 20.48
+ x = x / 0.1024
+ x = x * 8
+ x = x * 0.5
+ x = x / 0.001
+ Dim [922337203685477.5807 1] As Currency
+ [922337203685477.5807 1] = x + 5477.5807
+ TestUtil.AssertEqual(CStr([922337203685477.5807 1]), "922337203685477.5807", "CStr([922337203685477.5807 1])")
+ Dim [-922337203685477.5808 1] As Currency
+ [-922337203685477.5808 1] = -[922337203685477.5807 1] - 0.0001
+ TestUtil.AssertEqual(CStr([-922337203685477.5808 1]), "-922337203685477.5808", "CStr([-922337203685477.5808 1])")
+
+ x = -21474836.48@
+ y = 42949672.96@
+ Dim [-922337203685477.5808 2] As Currency
+ [-922337203685477.5808 2] = x * y
+ TestUtil.AssertEqual(CStr([-922337203685477.5808 2]), "-922337203685477.5808", "CStr([-922337203685477.5808 2])")
+
+ ' Check huge literals; FIXME: fails yet, because doubles are stored/read in compiler :(
+ ' TestUtil.AssertEqualStrict(922337203685477.5807@, [922337203685477.5807 1], "922337203685477.5807@")
+ ' x = 922337203685477.5807@
+ ' TestUtil.AssertEqualStrict(x, [922337203685477.5807 1], "x = 922337203685477.5807@")
+
+ ' Comparisons
+ TestUtil.Assert([-922337203685477.5808 1] = [-922337203685477.5808 2], [-922337203685477.5808 1] & " = " & [-922337203685477.5808 2])
+ TestUtil.Assert(Not([-922337203685477.5808 1] <> [-922337203685477.5808 2]), "Not(" & [-922337203685477.5808 1] & " <> " & [-922337203685477.5808 2] & ")")
+ TestUtil.Assert(Not([-922337203685477.5808 1] < [-922337203685477.5808 2]), "Not(" & [-922337203685477.5808 1] & " < " & [-922337203685477.5808 2] & ")")
+ TestUtil.Assert(Not([-922337203685477.5808 1] > [-922337203685477.5808 2]), "Not(" & [-922337203685477.5808 1] & " > " & [-922337203685477.5808 2] & ")")
+ TestUtil.Assert([-922337203685477.5808 1] <= [-922337203685477.5808 2], [-922337203685477.5808 1] & " <= " & [-922337203685477.5808 2])
+ TestUtil.Assert([-922337203685477.5808 1] >= [-922337203685477.5808 2], [-922337203685477.5808 1] & " >= " & [-922337203685477.5808 2])
+
+ TestUtil.Assert(Not([-922337203685477.5808 1] = [922337203685477.5807 1]), "Not(" & [-922337203685477.5808 1] & " = " & [922337203685477.5807 1] & ")")
+ TestUtil.Assert([-922337203685477.5808 1] <> [922337203685477.5807 1], [-922337203685477.5808 1] & " <> " & [922337203685477.5807 1])
+ TestUtil.Assert([-922337203685477.5808 1] < [922337203685477.5807 1], [-922337203685477.5808 1] & " < " & [922337203685477.5807 1])
+ TestUtil.Assert(Not([-922337203685477.5808 1] > [922337203685477.5807 1]), "Not(" & [-922337203685477.5808 1] & " > " & [922337203685477.5807 1] & ")")
+ TestUtil.Assert([-922337203685477.5808 1] <= [922337203685477.5807 1], [-922337203685477.5808 1] & " <= " & [922337203685477.5807 1])
+ TestUtil.Assert(Not([-922337203685477.5808 1] >= [922337203685477.5807 1]), "Not(" & [-922337203685477.5808 1] & " >= " & [922337203685477.5807 1] & ")")
+
+ ' Two close huge negative values
+ TestUtil.Assert(Not([-922337203685477.5808 1] = -[922337203685477.5807 1]), "Not(" & [-922337203685477.5808 1] & " = " & -[922337203685477.5807 1] & ")")
+ TestUtil.Assert([-922337203685477.5808 1] <> -[922337203685477.5807 1], [-922337203685477.5808 1] & " <> " & -[922337203685477.5807 1])
+ TestUtil.Assert([-922337203685477.5808 1] < -[922337203685477.5807 1], [-922337203685477.5808 1] & " < " & -[922337203685477.5807 1])
+ TestUtil.Assert(Not([-922337203685477.5808 1] > -[922337203685477.5807 1]), "Not(" & [-922337203685477.5808 1] & " > " & -[922337203685477.5807 1] & ")")
+ TestUtil.Assert([-922337203685477.5808 1] <= -[922337203685477.5807 1], [-922337203685477.5808 1] & " <= " & -[922337203685477.5807 1])
+ TestUtil.Assert(Not([-922337203685477.5808 1] >= -[922337203685477.5807 1]), "Not(" & [-922337203685477.5808 1] & " >= " & -[922337203685477.5807 1] & ")")
+
+ TestUtil.AssertEqual([-922337203685477.5808 1] + [922337203685477.5807 1], -0.0001@, [-922337203685477.5808 1] & " + " & [922337203685477.5807 1])
+
+ ' It is not possible to negate -922337203685477.5808, because 922337203685477.5808 is not representable (max is 922337203685477.5807)
+ verify_NegationOverflow([-922337203685477.5808 1])
+
+ ' Different overflows
+ verify_AddOverflow([922337203685477.5807 1], 0.0001@)
+ verify_AddOverflow([-922337203685477.5808 1], -0.0001@)
+
+ verify_SubOverflow([922337203685477.5807 1], -0.0001@)
+ verify_SubOverflow([-922337203685477.5808 1], 0.0001@)
+
+ verify_MulOverflow([922337203685477.5807 1], 1.1@)
+ verify_MulOverflow([-922337203685477.5808 1], 1.1@)
+
+ verify_DivOverflow([922337203685477.5807 1], 0.1@)
+ verify_DivOverflow([-922337203685477.5808 1], 0.1@)
+
+ x = 0.1@ ' Must round to 0 in Not, and complement is -1
+ TestUtil.AssertEqual(Not x, -1, "Not " & x)
+ x = 0.6@ ' Must round to 1 in Not, and complement is -2
+ TestUtil.AssertEqual(Not x, -2, "Not " & x)
+ ' TODO: fix compile-time constant operations: rounding is wrong
+ TestUtil.AssertEqual(Not 0.1@, -1, "Not 0.1@")
+ ' TestUtil.AssertEqual(Not 0.6@, -2, "Not 0.6@") ' Fails, gives -1
+
+ Exit Sub
+errorHandler:
+ TestUtil.ReportErrorHandler("verify_testCurrency", Err, Error$, Erl)
+End Sub
+
+Sub verify_DivideByZero(x As Currency, y As Currency)
+ On Error GoTo errorHandler
+
+ x = x / y
+ TestUtil.Assert(False, "verify_DivideByZero", x & " / " & y & ": Division by zero expected")
+
+ Exit Sub
+errorHandler:
+ TestUtil.AssertEqual(Err, 11, x & " / " & y & " gave " & Error$)
+End Sub
+
+Sub verify_IDivideByZero(x As Currency, y As Currency)
+ On Error GoTo errorHandler
+
+ x = x \ y
+ TestUtil.Assert(False, "verify_IDivideByZero", x & " \ " & y & ": Division by zero expected")
+
+ Exit Sub
+errorHandler:
+ TestUtil.AssertEqual(Err, 11, x & " \ " & y & " gave " & Error$)
+End Sub
+
+Sub verify_IModByZero(x As Currency, y As Currency)
+ On Error GoTo errorHandler
+
+ x = x Mod y
+ TestUtil.Assert(False, "verify_IModByZero", x & " Mod " & y & ": Division by zero expected")
+
+ Exit Sub
+errorHandler:
+ TestUtil.AssertEqual(Err, 11, x & " Mod " & y & " gave " & Error$)
+End Sub
+
+Sub verify_NegationOverflow(x As Currency)
+ On Error GoTo errorHandler
+
+ x = -x
+ TestUtil.Assert(False, "verify_NegationOverflow", "-" & x & ": Overflow expected")
+
+ Exit Sub
+errorHandler:
+ TestUtil.AssertEqual(Err, 6, "-" & x & " gave " & Error$)
+End Sub
+
+Sub verify_AddOverflow(x As Currency, y As Currency)
+ On Error GoTo errorHandler
+
+ x = x + y
+ TestUtil.Assert(False, "verify_AddOverflow", x & " + " & y & ": Overflow expected")
+
+ Exit Sub
+errorHandler:
+ TestUtil.AssertEqual(Err, 6, x & " + " & y & " gave " & Error$)
+End Sub
+
+Sub verify_SubOverflow(x As Currency, y As Currency)
+ On Error GoTo errorHandler
+
+ x = x - y
+ TestUtil.Assert(False, "verify_SubOverflow", x & " - " & y & ": Overflow expected")
+
+ Exit Sub
+errorHandler:
+ TestUtil.AssertEqual(Err, 6, x & " - " & y & " gave " & Error$)
+End Sub
+
+Sub verify_MulOverflow(x As Currency, y As Currency)
+ On Error GoTo errorHandler
+
+ x = x * y
+ TestUtil.Assert(False, "verify_MulOverflow", x & " * " & y & ": Overflow expected")
+
+ Exit Sub
+errorHandler:
+ TestUtil.AssertEqual(Err, 6, x & " * " & y & " gave " & Error$)
+End Sub
+
+Sub verify_DivOverflow(x As Currency, y As Currency)
+ On Error GoTo errorHandler
+
+ x = x / y
+ TestUtil.Assert(False, "verify_DivOverflow", x & " / " & y & ": Overflow expected")
+
+ Exit Sub
+errorHandler:
+ TestUtil.AssertEqual(Err, 6, x & " / " & y & " gave " & Error$)
+End Sub
diff --git a/basic/source/sbx/sbxbyte.cxx b/basic/source/sbx/sbxbyte.cxx
index 30387a4f1232..3e95c5124219 100644
--- a/basic/source/sbx/sbxbyte.cxx
+++ b/basic/source/sbx/sbxbyte.cxx
@@ -92,23 +92,20 @@ start:
nRes = static_cast<sal_uInt8>(p->nULong);
break;
case SbxCURRENCY:
+ nRes = CurTo<sal_uInt8>(p->nInt64);
+ break;
case SbxSALINT64:
- {
- sal_Int64 val = p->nInt64;
- if ( p->eType == SbxCURRENCY )
- val = val / CURRENCY_FACTOR;
- if( val > SbxMAXBYTE )
+ if (sal_Int64 val = p->nInt64; val > SbxMAXBYTE)
{
SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXBYTE;
}
- else if( p->nInt64 < 0 )
+ else if (val < 0)
{
SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
}
else
nRes = static_cast<sal_uInt8>(val);
break;
- }
case SbxSALUINT64:
if( p->uInt64 > SbxMAXBYTE )
{
@@ -249,7 +246,7 @@ void ImpPutByte( SbxValues* p, sal_uInt8 n )
case SbxDOUBLE:
p->nDouble = n; break;
case SbxCURRENCY:
- p->nInt64 = n * CURRENCY_FACTOR; break;
+ p->nInt64 = CurFrom(n); break;
case SbxSALINT64:
p->nInt64 = n; break;
case SbxSALUINT64:
@@ -298,7 +295,7 @@ void ImpPutByte( SbxValues* p, sal_uInt8 n )
case SbxBYREF | SbxDOUBLE:
*p->pDouble = n; break;
case SbxBYREF | SbxCURRENCY:
- p->nInt64 = n * CURRENCY_FACTOR; break;
+ p->nInt64 = CurFrom(n); break;
case SbxBYREF | SbxSALINT64:
*p->pnInt64 = n; break;
case SbxBYREF | SbxSALUINT64:
diff --git a/basic/source/sbx/sbxchar.cxx b/basic/source/sbx/sbxchar.cxx
index 466b16f143b2..6bc44b4e138a 100644
--- a/basic/source/sbx/sbxchar.cxx
+++ b/basic/source/sbx/sbxchar.cxx
@@ -74,25 +74,20 @@ start:
nRes = static_cast<sal_Unicode>(p->nULong);
break;
case SbxCURRENCY:
+ nRes = CurTo<sal_Unicode>(p->nInt64);
+ break;
case SbxSALINT64:
- {
- sal_Int64 val = p->nInt64;
-
- if ( p->eType == SbxCURRENCY )
- val = val / CURRENCY_FACTOR;
-
- if( val > SbxMAXCHAR )
+ if (sal_Int64 val = p->nInt64; val > SbxMAXCHAR)
{
SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXCHAR;
}
- else if( p->nInt64 < SbxMINCHAR )
+ else if (val < SbxMINCHAR)
{
SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMINCHAR;
}
else
nRes = static_cast<sal_Unicode>(val);
break;
- }
case SbxSALUINT64:
if( p->uInt64 > SbxMAXCHAR )
{
@@ -227,7 +222,7 @@ start:
case SbxDOUBLE:
p->nDouble = n; break;
case SbxCURRENCY:
- p->nInt64 = n * CURRENCY_FACTOR; break;
+ p->nInt64 = CurFrom(n); break;
case SbxSALINT64:
p->nInt64 = n; break;
case SbxSALUINT64:
@@ -285,7 +280,7 @@ start:
case SbxBYREF | SbxDOUBLE:
*p->pDouble = static_cast<double>(n); break;
case SbxBYREF | SbxCURRENCY:
- p->nInt64 = n * CURRENCY_FACTOR; break;
+ p->nInt64 = CurFrom(n); break;
case SbxBYREF | SbxSALINT64:
*p->pnInt64 = n; break;
case SbxBYREF | SbxSALUINT64:
diff --git a/basic/source/sbx/sbxconv.hxx b/basic/source/sbx/sbxconv.hxx
index 8f11122bd319..f32e5fc890d7 100644
--- a/basic/source/sbx/sbxconv.hxx
+++ b/basic/source/sbx/sbxconv.hxx
@@ -26,6 +26,7 @@
#include <basic/sbxdef.hxx>
#include <o3tl/float_int_conversion.hxx>
+#include <o3tl/safeint.hxx>
#include <rtl/math.hxx>
#include <sal/types.h>
@@ -107,14 +108,76 @@ void ImpPutCurrency( SbxValues*, const sal_Int64 );
inline sal_Int64 ImpDoubleToCurrency( double d )
{
- if (d > 0)
- return static_cast<sal_Int64>( d * CURRENCY_FACTOR + 0.5);
- else
- return static_cast<sal_Int64>( d * CURRENCY_FACTOR - 0.5);
+ double result = d > 0 ? (d * CURRENCY_FACTOR + 0.5) : (d * CURRENCY_FACTOR - 0.5);
+ if (result >= double(SAL_MAX_INT64)) // double(SAL_MAX_INT64) is greater than SAL_MAX_INT64
+ {
+ SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
+ return SAL_MAX_INT64;
+ }
+ if (result < double(SAL_MIN_INT64))
+ {
+ SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
+ return SAL_MIN_INT64;
+ }
+ return result;
+}
+
+template <typename I>
+ requires std::is_integral_v<I>
+inline sal_Int64 CurFrom(I n)
+{
+ using ValidRange = o3tl::ValidRange<sal_Int64, SAL_MIN_INT64 / CURRENCY_FACTOR, SAL_MAX_INT64 / CURRENCY_FACTOR>;
+ if (ValidRange::isAbove(n))
+ {
+ SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
+ return SAL_MAX_INT64;
+ }
+ if (ValidRange::isBelow(n))
+ {
+ SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
+ return SAL_MIN_INT64;
+ }
+ return n * CURRENCY_FACTOR;
}
-inline double ImpCurrencyToDouble( const sal_Int64 r )
- { return static_cast<double>(r) / double(CURRENCY_FACTOR); }
+inline double ImpCurrencyToDouble(sal_Int64 r) { return static_cast<double>(r) / CURRENCY_FACTOR; }
+
+template <typename I>
+ requires std::is_integral_v<I>
+inline I CurTo(sal_Int64 cur_val)
+{
+ sal_Int64 i = CurTo<sal_Int64>(cur_val);
+ if (o3tl::ValidRange<I>::isAbove(i))
+ {
+ SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
+ return std::numeric_limits<I>::max();
+ }
+ if (o3tl::ValidRange<I>::isBelow(i))
+ {
+ SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
+ return std::numeric_limits<I>::min();
+ }
+ return i;
+}
+
+template <> inline sal_Int64 CurTo<sal_Int64>(sal_Int64 cur_val)
+{
+ sal_Int64 i = cur_val / CURRENCY_FACTOR;
+ // Rounding (half-to-even)
+ int f = cur_val % CURRENCY_FACTOR;
+ if (i % 2 == 1)
+ {
+ if (f < 0)
+ --f;
+ else
+ ++f;
+ }
+ if (f > CURRENCY_FACTOR / 2)
+ ++i;
+ else if (f < -CURRENCY_FACTOR / 2)
+ --i;
+ return i;
+}
// SBXDEC.CXX
diff --git a/basic/source/sbx/sbxcurr.cxx b/basic/source/sbx/sbxcurr.cxx
index 5fcab97243ca..d5fe4be9f6b7 100644
--- a/basic/source/sbx/sbxcurr.cxx
+++ b/basic/source/sbx/sbxcurr.cxx
@@ -104,25 +104,7 @@ static sal_Int64 ImpStringToCurrency(const rtl::OUString& rStr)
SbxBase::SetError(ERRCODE_BASIC_CONVERSION);
}
- sal_Int64 nRes = 0;
- const auto fShiftedResult = fResult * CURRENCY_FACTOR;
- if (fShiftedResult + 0.5 > static_cast<double>(SAL_MAX_INT64)
- || fShiftedResult - 0.5 < static_cast<double>(SAL_MIN_INT64))
- {
- nRes = SAL_MAX_INT64;
- if (fShiftedResult - 0.5 < static_cast<double>(SAL_MIN_INT64))
- {
- nRes = SAL_MIN_INT64;
- }
-
- SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
- }
- else
- {
- nRes = ImpDoubleToCurrency(fResult);
- }
-
- return nRes;
+ return ImpDoubleToCurrency(fResult);
}
sal_Int64 ImpGetCurrency( const SbxValues* p )
@@ -141,78 +123,36 @@ start:
case SbxCURRENCY:
nRes = p->nInt64; break;
case SbxBYTE:
- nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nByte);
+ nRes = CurFrom(p->nByte);
break;
case SbxCHAR:
- nRes = sal_Int64(CURRENCY_FACTOR) * reinterpret_cast<sal_Int64>(p->pChar);
+ nRes = CurFrom(p->nChar);
break;
case SbxBOOL:
case SbxINTEGER:
- nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nInteger);
+ nRes = CurFrom(p->nInteger);
break;
case SbxUSHORT:
- nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nUShort);
+ nRes = CurFrom(p->nUShort);
break;
case SbxLONG:
- nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nLong);
+ nRes = CurFrom(p->nLong);
break;
case SbxULONG:
- nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nULong);
+ nRes = CurFrom(p->nULong);
break;
-
case SbxSALINT64:
- {
- nRes = p->nInt64 * CURRENCY_FACTOR; break;
-#if 0
- // Huh, is the 'break' above intentional? That means this
- // is unreachable, obviously. Avoid warning by ifdeffing
- // this out for now. Do not delete this #if 0 block unless
- // you know for sure the 'break' above is intentional.
- if ( nRes > SAL_MAX_INT64 )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SAL_MAX_INT64;
- }
-#endif
- }
+ nRes = CurFrom(p->nInt64);
+ break;
case SbxSALUINT64:
- nRes = p->nInt64 * CURRENCY_FACTOR; break;
-#if 0
- // As above
- if ( nRes > SAL_MAX_INT64 )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SAL_MAX_INT64;
- }
- else if ( nRes < SAL_MIN_INT64 )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SAL_MIN_INT64;
- }
+ nRes = CurFrom(p->uInt64);
break;
-#endif
-//TODO: bring back SbxINT64 types here for limits -1 with flag value at SAL_MAX/MIN
case SbxSINGLE:
- if( p->nSingle * CURRENCY_FACTOR + 0.5 > float(SAL_MAX_INT64)
- || p->nSingle * CURRENCY_FACTOR - 0.5 < float(SAL_MIN_INT64) )
- {
- nRes = SAL_MAX_INT64;
- if( p->nSingle * CURRENCY_FACTOR - 0.5 < float(SAL_MIN_INT64) )
- nRes = SAL_MIN_INT64;
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
- break;
- }
- nRes = ImpDoubleToCurrency( static_cast<double>(p->nSingle) );
+ nRes = ImpDoubleToCurrency(p->nSingle);
break;
case SbxDATE:
case SbxDOUBLE:
- if( p->nDouble * CURRENCY_FACTOR + 0.5 > double(SAL_MAX_INT64)
- || p->nDouble * CURRENCY_FACTOR - 0.5 < double(SAL_MIN_INT64) )
- {
- nRes = SAL_MAX_INT64;
- if( p->nDouble * CURRENCY_FACTOR - 0.5 < double(SAL_MIN_INT64) )
- nRes = SAL_MIN_INT64;
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
- break;
- }
nRes = ImpDoubleToCurrency( p->nDouble );
break;
@@ -249,18 +189,18 @@ start:
}
case SbxBYREF | SbxCHAR:
- nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(*p->pChar);
+ nRes = CurFrom(*p->pChar);
break;
case SbxBYREF | SbxBYTE:
- nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(*p->pByte);
+ nRes = CurFrom(*p->pByte);
break;
case SbxBYREF | SbxBOOL:
case SbxBYREF | SbxINTEGER:
- nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(*p->pInteger);
+ nRes = CurFrom(*p->pInteger);
break;
case SbxBYREF | SbxERROR:
case SbxBYREF | SbxUSHORT:
- nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(*p->pUShort);
+ nRes = CurFrom(*p->pUShort);
break;
// from here on had to be tested
@@ -289,42 +229,17 @@ start:
return nRes;
}
-
void ImpPutCurrency( SbxValues* p, const sal_Int64 r )
{
- SbxValues aTmp;
-start:
switch( +p->eType )
{
- // Here are tests necessary
- case SbxCHAR:
- aTmp.pChar = &p->nChar; goto direct;
- case SbxBYTE:
- aTmp.pByte = &p->nByte; goto direct;
- case SbxINTEGER:
- case SbxBOOL:
- aTmp.pInteger = &p->nInteger; goto direct;
- case SbxLONG:
- aTmp.pLong = &p->nLong; goto direct;
- case SbxULONG:
- aTmp.pULong = &p->nULong; goto direct;
- case SbxERROR:
- case SbxUSHORT:
- aTmp.pUShort = &p->nUShort; goto direct;
- direct:
- aTmp.eType = SbxDataType( p->eType | SbxBYREF );
- p = &aTmp; goto start;
-
- // from here no longer
- case SbxSINGLE:
- p->nSingle = static_cast<float>( r / CURRENCY_FACTOR ); break;
case SbxDATE:
case SbxDOUBLE:
p->nDouble = ImpCurrencyToDouble( r ); break;
case SbxSALUINT64:
- p->uInt64 = r / CURRENCY_FACTOR; break;
+ p->uInt64 = CurTo<sal_uInt64>(r); break;
case SbxSALINT64:
- p->nInt64 = r / CURRENCY_FACTOR; break;
+ p->nInt64 = CurTo<sal_Int64>(r); break;
case SbxCURRENCY:
p->nInt64 = r; break;
@@ -333,7 +248,7 @@ start:
case SbxBYREF | SbxDECIMAL:
{
SbxDecimal* pDec = ImpCreateDecimal( p );
- if( !pDec->setDouble( ImpCurrencyToDouble( r ) / CURRENCY_FACTOR ) )
+ if( !pDec->setDouble( ImpCurrencyToDouble( r ) ) )
SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
break;
}
@@ -354,94 +269,43 @@ start:
SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT );
break;
}
+ case SbxCHAR:
+ p->nChar = CurTo<sal_Unicode>(r); break;
case SbxBYREF | SbxCHAR:
- {
- sal_Int64 val = r / CURRENCY_FACTOR;
- if( val > SbxMAXCHAR )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXCHAR;
- }
- else if( val < SbxMINCHAR )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMINCHAR;
- }
- *p->pChar = static_cast<sal_Unicode>(val); break;
- }
+ *p->pChar = CurTo<sal_Unicode>(r); break;
+ case SbxBYTE:
+ p->nByte = CurTo<sal_uInt8>(r); break;
case SbxBYREF | SbxBYTE:
- {
- sal_Int64 val = r / CURRENCY_FACTOR;
- if( val > SbxMAXBYTE )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXBYTE;
- }
- else if( val < 0 )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = 0;
- }
- *p->pByte = static_cast<sal_uInt8>(val); break;
- }
+ *p->pByte = CurTo<sal_uInt8>(r); break;
+ case SbxINTEGER:
+ case SbxBOOL:
+ p->nInteger = CurTo<sal_Int16>(r); break;
case SbxBYREF | SbxINTEGER:
case SbxBYREF | SbxBOOL:
- {
- sal_Int64 val = r / CURRENCY_FACTOR;
- if( r > SbxMAXINT )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXINT;
- }
- else if( r < SbxMININT )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMININT;
- }
- *p->pInteger = static_cast<sal_uInt16>(val); break;
- }
+ *p->pInteger = CurTo<sal_Int16>(r); break;
+ case SbxERROR:
+ case SbxUSHORT:
+ p->nUShort = CurTo<sal_uInt16>(r); break;
case SbxBYREF | SbxERROR:
case SbxBYREF | SbxUSHORT:
- {
- sal_Int64 val = r / CURRENCY_FACTOR;
- if( val > SbxMAXUINT )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXUINT;
- }
- else if( val < 0 )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = 0;
- }
- *p->pUShort = static_cast<sal_uInt16>(val); break;
- }
+ *p->pUShort = CurTo<sal_uInt16>(r); break;
+ case SbxLONG:
+ p->nLong = CurTo<sal_Int32>(r); break;
case SbxBYREF | SbxLONG:
- {
- sal_Int64 val = r / CURRENCY_FACTOR;
- if( val > SbxMAXLNG )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXLNG;
- }
- else if( val < SbxMINLNG )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMINLNG;
- }
- *p->pLong = static_cast<sal_Int32>(val); break;
- }
+ *p->pLong = CurTo<sal_Int32>(r); break;
+ case SbxULONG:
+ p->nULong = CurTo<sal_uInt32>(r); break;
case SbxBYREF | SbxULONG:
- {
- sal_Int64 val = r / CURRENCY_FACTOR;
- if( val > SbxMAXULNG )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXULNG;
- }
- else if( val < 0 )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = 0;
- }
- *p->pULong = static_cast<sal_uInt32>(val); break;
- }
+ *p->pULong = CurTo<sal_uInt32>(r); break;
case SbxBYREF | SbxCURRENCY:
*p->pnInt64 = r; break;
case SbxBYREF | SbxSALINT64:
- *p->pnInt64 = r / CURRENCY_FACTOR; break;
+ *p->pnInt64 = CurTo<sal_Int64>(r); break;
case SbxBYREF | SbxSALUINT64:
- *p->puInt64 = static_cast<sal_uInt64>(r) / CURRENCY_FACTOR; break;
+ *p->puInt64 = CurTo<sal_uInt64>(r); break;
+ case SbxSINGLE:
case SbxBYREF | SbxSINGLE:
- p->nSingle = static_cast<float>( r / CURRENCY_FACTOR ); break;
+ p->nSingle = r / float(CURRENCY_FACTOR); break;
case SbxBYREF | SbxDATE:
case SbxBYREF | SbxDOUBLE:
*p->pDouble = ImpCurrencyToDouble( r ); break;
diff --git a/basic/source/sbx/sbxint.cxx b/basic/source/sbx/sbxint.cxx
index 14b1727433d4..6a6721eb7840 100644
--- a/basic/source/sbx/sbxint.cxx
+++ b/basic/source/sbx/sbxint.cxx
@@ -78,21 +78,8 @@ start:
nRes = ImpDoubleToInteger(p->nSingle);
break;
case SbxCURRENCY:
- {
- sal_Int64 tstVal = p->nInt64 / sal_Int64(CURRENCY_FACTOR);
-
- if( tstVal > SbxMAXINT )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXINT;
- }
- else if( tstVal < SbxMININT )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMININT;
- }
- else
- nRes = static_cast<sal_Int16>(tstVal);
- break;
- }
+ nRes = CurTo<sal_Int16>(p->nInt64);
+ break;
case SbxSALINT64:
if( p->nInt64 > SbxMAXINT )
{
@@ -227,7 +214,7 @@ start:
case SbxDOUBLE:
p->nDouble = n; break;
case SbxCURRENCY:
- p->nInt64 = n * CURRENCY_FACTOR; break;
+ p->nInt64 = CurFrom(n); break;
case SbxSALINT64:
p->nInt64 = n; break;
case SbxDECIMAL:
@@ -286,7 +273,7 @@ start:
}
*p->pULong = static_cast<sal_uInt32>(n); break;
case SbxBYREF | SbxCURRENCY:
- *p->pnInt64 = n * CURRENCY_FACTOR; break;
+ *p->pnInt64 = CurFrom(n); break;
case SbxBYREF | SbxSALINT64:
*p->pnInt64 = n; break;
case SbxBYREF | SbxSALUINT64:
@@ -357,7 +344,7 @@ start:
nRes = ImpDoubleToSalInt64(p->nDouble);
break;
case SbxCURRENCY:
- nRes = p->nInt64 / CURRENCY_FACTOR; break;
+ nRes = CurTo<sal_Int64>(p->nInt64); break;
case SbxSALINT64:
nRes = p->nInt64; break;
case SbxSALUINT64:
@@ -413,7 +400,7 @@ start:
case SbxBYREF | SbxULONG:
nRes = *p->pULong; break;
case SbxBYREF | SbxCURRENCY:
- nRes = p->nInt64 / CURRENCY_FACTOR; break;
+ nRes = CurTo<sal_Int64>(*p->pnInt64); break;
case SbxBYREF | SbxSALINT64:
nRes = *p->pnInt64; break;
@@ -564,7 +551,7 @@ start:
case SbxBYREF | SbxDOUBLE:
*p->pDouble = static_cast<double>(n); break;
case SbxBYREF | SbxCURRENCY:
- *p->pnInt64 = n * CURRENCY_FACTOR; break;
+ *p->pnInt64 = CurFrom(n); break;
case SbxBYREF | SbxSALINT64:
*p->pnInt64 = n; break;
case SbxBYREF | SbxSALUINT64:
@@ -612,7 +599,7 @@ start:
nRes = ImpDoubleToSalUInt64(p->nDouble);
break;
case SbxCURRENCY:
- nRes = p->nInt64 * CURRENCY_FACTOR; break;
+ nRes = CurFrom(p->nInt64); break;
case SbxSALINT64:
if( p->nInt64 < 0 )
{
@@ -797,12 +784,8 @@ start:
*p->pDouble = ImpSalUInt64ToDouble( n ); break;
case SbxBYREF | SbxCURRENCY:
- if ( n > ( SAL_MAX_INT64 / CURRENCY_FACTOR ) )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
- n = SAL_MAX_INT64;
- }
- *p->pnInt64 = static_cast<sal_Int64>( n * CURRENCY_FACTOR ); break;
+ *p->pnInt64 = CurFrom(n);
+ break;
case SbxBYREF | SbxSALUINT64:
*p->puInt64 = n; break;
case SbxBYREF | SbxSALINT64:
diff --git a/basic/source/sbx/sbxlng.cxx b/basic/source/sbx/sbxlng.cxx
index a49b6928758c..7d37473da915 100644
--- a/basic/source/sbx/sbxlng.cxx
+++ b/basic/source/sbx/sbxlng.cxx
@@ -67,14 +67,8 @@ start:
nRes = p->uInt64;
break;
case SbxCURRENCY:
- {
- sal_Int64 tstVal = p->nInt64 / CURRENCY_FACTOR;
- nRes = static_cast<sal_Int32>(tstVal);
- if( tstVal < SbxMINLNG || SbxMAXLNG < tstVal ) SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW );
- if( SbxMAXLNG < tstVal ) nRes = SbxMAXLNG;
- if( tstVal < SbxMINLNG ) nRes = SbxMINLNG;
+ nRes = CurTo<sal_Int32>(p->nInt64);
break;
- }
case SbxDATE:
case SbxDOUBLE:
case SbxDECIMAL:
@@ -192,7 +186,7 @@ start:
case SbxDOUBLE:
p->nDouble = n; break;
case SbxCURRENCY:
- p->nInt64 = n * CURRENCY_FACTOR; break;
+ p->nInt64 = CurFrom(n); break;
case SbxSALINT64:
p->nInt64 = n; break;
case SbxDECIMAL:
@@ -282,7 +276,7 @@ start:
case SbxBYREF | SbxDOUBLE:
*p->pDouble = static_cast<double>(n); break;
case SbxBYREF | SbxCURRENCY:
- *p->pnInt64 = static_cast<sal_Int64>(n) * sal_Int64(CURRENCY_FACTOR); break;
+ *p->pnInt64 = CurFrom(n); break;
default:
SbxBase::SetError( ERRCODE_BASIC_CONVERSION );
}
diff --git a/basic/source/sbx/sbxuint.cxx b/basic/source/sbx/sbxuint.cxx
index 2f4f369607bd..4593f72d22d3 100644
--- a/basic/source/sbx/sbxuint.cxx
+++ b/basic/source/sbx/sbxuint.cxx
@@ -74,16 +74,7 @@ start:
nRes = static_cast<sal_uInt16>(p->nULong);
break;
case SbxCURRENCY:
- if( p->nInt64 / CURRENCY_FACTOR > SbxMAXUINT )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SbxMAXUINT;
- }
- else if( p->nInt64 < 0 )
- {
- SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = 0;
- }
- else
- nRes = static_cast<sal_uInt16>(p->nInt64 / CURRENCY_FACTOR);
+ nRes = CurTo<sal_uInt16>(p->nInt64);
break;
case SbxSALINT64:
if( p->nInt64 > SbxMAXUINT )
@@ -209,7 +200,7 @@ start:
case SbxDOUBLE:
p->nDouble = n; break;
case SbxCURRENCY:
- p->nInt64 = n * CURRENCY_FACTOR; break;
+ p->nInt64 = CurFrom(n); break;
case SbxSALINT64:
p->nInt64 = n; break;
case SbxSALUINT64:
@@ -276,7 +267,7 @@ start:
case SbxBYREF | SbxDOUBLE:
*p->pDouble = n; break;
case SbxBYREF | SbxCURRENCY:
- *p->pnInt64 = n * CURRENCY_FACTOR; break;
+ *p->pnInt64 = CurFrom(n); break;
case SbxBYREF | SbxSALINT64:
*p->pnInt64 = n; break;
case SbxBYREF | SbxSALUINT64:
diff --git a/basic/source/sbx/sbxulng.cxx b/basic/source/sbx/sbxulng.cxx
index 8ccde8088006..5287cba7b9ae 100644
--- a/basic/source/sbx/sbxulng.cxx
+++ b/basic/source/sbx/sbxulng.cxx
@@ -252,7 +252,7 @@ start:
case SbxBYREF | SbxDOUBLE:
*p->pDouble = n; break;
case SbxBYREF | SbxCURRENCY:
- *p->pnInt64 = n * CURRENCY_FACTOR; break;
+ *p->pnInt64 = CurFrom(n); break;
case SbxBYREF | SbxSALINT64:
*p->pnInt64 = n; break;
case SbxBYREF | SbxSALUINT64:
diff --git a/basic/source/sbx/sbxvalue.cxx b/basic/source/sbx/sbxvalue.cxx
index ca1264609ed8..6587d08f5da4 100644
--- a/basic/source/sbx/sbxvalue.cxx
+++ b/basic/source/sbx/sbxvalue.cxx
@@ -24,6 +24,7 @@
#include <osl/diagnose.h>
#include <o3tl/float_int_conversion.hxx>
+#include <o3tl/safeint.hxx>
#include <tools/debug.hxx>
#include <tools/stream.hxx>
#include <sal/log.hxx>
@@ -771,6 +772,40 @@ bool SbxValue::Convert( SbxDataType eTo )
}
////////////////////////////////// Calculating
+static sal_Int64 MulAndDiv(sal_Int64 n, sal_Int64 mul, sal_Int64 div)
+{
+ if (div == 0)
+ {
+ SbxBase::SetError(ERRCODE_BASIC_ZERODIV);
+ return n;
+ }
+ auto errorValue = [](sal_Int64 x, sal_Int64 y, sal_Int64 z)
+ {
+ const int i = (x < 0 ? -1 : 1) * (y < 0 ? -1 : 1) * (z < 0 ? -1 : 1);
+ return i == 1 ? SAL_MAX_INT64 : SAL_MIN_INT64;
+ };
+ sal_Int64 result;
+ // If x * integral part of (mul/div) overflows -> product does not fit
+ if (o3tl::checked_multiply(n, mul / div, result))
+ {
+ SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
+ return errorValue(n, mul, div);
+ }
+ if (sal_Int64 mul_frac = mul % div)
+ {
+ // can't overflow: mul_frac < div
+ sal_Int64 result_frac = n / div * mul_frac;
+ if (sal_Int64 x_frac = n % div)
+ result_frac += x_frac * mul_frac / div;
+ if (o3tl::checked_add(result, result_frac, result))
+ {
+ SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
+ return errorValue(n, mul, div);
+ }
+ }
+ return result;
+}
+
bool SbxValue::Compute( SbxOperator eOp, const SbxValue& rOp )
{
#if !HAVE_FEATURE_SCRIPTING
@@ -841,9 +876,10 @@ bool SbxValue::Compute( SbxOperator eOp, const SbxValue& rOp )
{
if( GetType() == eOpType )
{
- if( GetType() == SbxSALUINT64 || GetType() == SbxSALINT64
- || GetType() == SbxCURRENCY || GetType() == SbxULONG )
+ if (GetType() == SbxSALUINT64 || GetType() == SbxSALINT64 || GetType() == SbxULONG)
aL.eType = aR.eType = GetType();
+ else if (GetType() == SbxCURRENCY)
+ aL.eType = aR.eType = SbxSALINT64; // Convert to integer value before operation
// tdf#145960 - return type of boolean operators should be of type boolean
else if ( eOpType == SbxBOOL && eOp != SbxMOD && eOp != SbxIDIV )
aL.eType = aR.eType = SbxBOOL;
@@ -853,9 +889,9 @@ bool SbxValue::Compute( SbxOperator eOp, const SbxValue& rOp )
else
aL.eType = aR.eType = SbxLONG;
- if( rOp.Get( aR ) ) // re-do Get after type assigns above
+ if (rOp.Get(aR) && Get(aL)) // re-do Get after type assigns above
{
- if( Get( aL ) ) switch( eOp )
+ switch( eOp )
{
/* TODO: For SbxEMPTY operands with boolean operators use
* the VBA Nothing definition of Comparing Nullable Types?
@@ -868,16 +904,10 @@ bool SbxValue::Compute( SbxOperator eOp, const SbxValue& rOp )
* string.
*/
case SbxIDIV:
- if( aL.eType == SbxCURRENCY )
- if( !aR.nInt64 ) SetError( ERRCODE_BASIC_ZERODIV );
- else {
- aL.nInt64 /= aR.nInt64;
- aL.nInt64 *= CURRENCY_FACTOR;
- }
- else if( aL.eType == SbxSALUINT64 )
+ if( aL.eType == SbxSALUINT64 )
if( !aR.uInt64 ) SetError( ERRCODE_BASIC_ZERODIV );
else aL.uInt64 /= aR.uInt64;
- else if( aL.eType == SbxSALINT64 )
+ else if( aL.eType == SbxCURRENCY || aL.eType == SbxSALINT64 )
if( !aR.nInt64 ) SetError( ERRCODE_BASIC_ZERODIV );
else aL.nInt64 /= aR.nInt64;
else if( aL.eType == SbxLONG )
@@ -990,92 +1020,34 @@ bool SbxValue::Compute( SbxOperator eOp, const SbxValue& rOp )
}
else if( GetType() == SbxCURRENCY || rOp.GetType() == SbxCURRENCY )
{
- aL.eType = SbxCURRENCY;
- aR.eType = SbxCURRENCY;
+ aL.eType = aR.eType = SbxCURRENCY;
- if( rOp.Get( aR ) )
+ if (rOp.Get(aR) && Get(aL))
{
- if( Get( aL ) ) switch( eOp )
+ switch (eOp)
{
case SbxMUL:
- {
- // first overflow check: see if product will fit - test real value of product (hence 2 curr factors)
- double dTest = static_cast<double>(aL.nInt64) * static_cast<double>(aR.nInt64) / double(CURRENCY_FACTOR_SQUARE);
- if( dTest < SbxMINCURR || SbxMAXCURR < dTest)
- {
- aL.nInt64 = SAL_MAX_INT64;
- if( dTest < SbxMINCURR ) aL.nInt64 = SAL_MIN_INT64;
- SetError( ERRCODE_BASIC_MATH_OVERFLOW );
- break;
- }
- // second overflow check: see if unscaled product overflows - if so use doubles
- dTest = static_cast<double>(aL.nInt64) * static_cast<double>(aR.nInt64);
- if( !(o3tl::convertsToAtLeast(dTest, SAL_MIN_INT64)
- && o3tl::convertsToAtMost(dTest, SAL_MAX_INT64)))
- {
- aL.nInt64 = static_cast<sal_Int64>( dTest / double(CURRENCY_FACTOR) );
- break;
- }
- // precise calc: multiply then scale back (move decimal pt)
- aL.nInt64 *= aR.nInt64;
- aL.nInt64 /= CURRENCY_FACTOR;
- break;
- }
+ aL.nInt64 = MulAndDiv(aL.nInt64, aR.nInt64, CURRENCY_FACTOR);
+ break;
case SbxDIV:
- {
- if( !aR.nInt64 )
- {
- SetError( ERRCODE_BASIC_ZERODIV );
- break;
- }
- // first overflow check: see if quotient will fit - calc real value of quotient (curr factors cancel)
- double dTest = static_cast<double>(aL.nInt64) / static_cast<double>(aR.nInt64);
- if( dTest < SbxMINCURR || SbxMAXCURR < dTest)
- {
- SetError( ERRCODE_BASIC_MATH_OVERFLOW );
- break;
- }
- // second overflow check: see if scaled dividend overflows - if so use doubles
- dTest = static_cast<double>(aL.nInt64) * double(CURRENCY_FACTOR);
- if( !(o3tl::convertsToAtLeast(dTest, SAL_MIN_INT64)
- && o3tl::convertsToAtMost(dTest, SAL_MAX_INT64)))
- {
- aL.nInt64 = static_cast<sal_Int64>(dTest / static_cast<double>(aR.nInt64));
- break;
- }
- // precise calc: scale (move decimal pt) then divide
- aL.nInt64 *= CURRENCY_FACTOR;
- aL.nInt64 /= aR.nInt64;
- break;
- }
+ aL.nInt64 = MulAndDiv(aL.nInt64, CURRENCY_FACTOR, aR.nInt64);
+ break;
case SbxPLUS:
- {
- double dTest = ( static_cast<double>(aL.nInt64) + static_cast<double>(aR.nInt64) ) / double(CURRENCY_FACTOR);
- if( dTest < SbxMINCURR || SbxMAXCURR < dTest)
- {
- SetError( ERRCODE_BASIC_MATH_OVERFLOW );
- break;
- }
- aL.nInt64 += aR.nInt64;
- break;
- }
+ if (o3tl::checked_add(aL.nInt64, aR.nInt64, aL.nInt64))
+ SetError(ERRCODE_BASIC_MATH_OVERFLOW);
+ break;
- case SbxMINUS:
- {
- double dTest = ( static_cast<double>(aL.nInt64) - static_cast<double>(aR.nInt64) ) / double(CURRENCY_FACTOR);
- if( dTest < SbxMINCURR || SbxMAXCURR < dTest)
- {
- SetError( ERRCODE_BASIC_MATH_OVERFLOW );
- break;
- }
- aL.nInt64 -= aR.nInt64;
- break;
- }
case SbxNEG:
- aL.nInt64 = -aL.nInt64;
+ // Use subtraction; allows to detect negation of SAL_MIN_INT64
+ aR.nInt64 = std::exchange(aL.nInt64, 0);
+ [[fallthrough]];
+ case SbxMINUS:
+ if (o3tl::checked_sub(aL.nInt64, aR.nInt64, aL.nInt64))
+ SetError(ERRCODE_BASIC_MATH_OVERFLOW);
break;
+
default:
SetError( ERRCODE_BASIC_BAD_ARGUMENT );
}
@@ -1142,6 +1114,29 @@ Lbl_OpIsEmpty:
// The comparison routine deliver TRUE or FALSE.
+template <typename T> static bool CompareNormal(const T& l, const T& r, SbxOperator eOp)
+{
+ switch (eOp)
+ {
+ case SbxEQ:
+ return l == r;
+ case SbxNE:
+ return l != r;
+ case SbxLT:
+ return l < r;
+ case SbxGT:
+ return l > r;
+ case SbxLE:
+ return l <= r;
+ case SbxGE:
+ return l >= r;
+ default:
+ assert(false);
+ }
+ SbxBase::SetError(ERRCODE_BASIC_BAD_ARGUMENT);
+ return false;
+}
+
bool SbxValue::Compare( SbxOperator eOp, const SbxValue& rOp ) const
{
#if !HAVE_FEATURE_SCRIPTING
@@ -1184,23 +1179,8 @@ bool SbxValue::Compare( SbxOperator eOp, const SbxValue& rOp ) const
if( GetType() == SbxSTRING || rOp.GetType() == SbxSTRING )
{
aL.eType = aR.eType = SbxSTRING;
- if( Get( aL ) && rOp.Get( aR ) ) switch( eOp )
- {
- case SbxEQ:
- bRes = ( *aL.pOUString == *aR.pOUString ); break;
- case SbxNE:
- bRes = ( *aL.pOUString != *aR.pOUString ); break;
- case SbxLT:
- bRes = ( *aL.pOUString < *aR.pOUString ); break;
- case SbxGT:
- bRes = ( *aL.pOUString > *aR.pOUString ); break;
- case SbxLE:
- bRes = ( *aL.pOUString <= *aR.pOUString ); break;
- case SbxGE:
- bRes = ( *aL.pOUString >= *aR.pOUString ); break;
- default:
- SetError( ERRCODE_BASIC_BAD_ARGUMENT );
- }
+ if (Get(aL) && rOp.Get(aR))
+ bRes = CompareNormal(*aL.pOUString, *aR.pOUString, eOp);
}
// From 1995-12-19: If SbxSINGLE participate, then convert to SINGLE,
// otherwise it shows a numeric error
@@ -1208,23 +1188,7 @@ bool SbxValue::Compare( SbxOperator eOp, const SbxValue& rOp ) const
{
aL.eType = aR.eType = SbxSINGLE;
if( Get( aL ) && rOp.Get( aR ) )
- switch( eOp )
- {
- case SbxEQ:
- bRes = ( aL.nSingle == aR.nSingle ); break;
- case SbxNE:
- bRes = ( aL.nSingle != aR.nSingle ); break;
- case SbxLT:
- bRes = ( aL.nSingle < aR.nSingle ); break;
- case SbxGT:
- bRes = ( aL.nSingle > aR.nSingle ); break;
- case SbxLE:
- bRes = ( aL.nSingle <= aR.nSingle ); break;
- case SbxGE:
- bRes = ( aL.nSingle >= aR.nSingle ); break;
- default:
- SetError( ERRCODE_BASIC_BAD_ARGUMENT );
- }
+ bRes = CompareNormal(aL.nSingle, aR.nSingle, eOp);
}
else if( GetType() == SbxDECIMAL && rOp.GetType() == SbxDECIMAL )
{
@@ -1259,6 +1223,12 @@ bool SbxValue::Compare( SbxOperator eOp, const SbxValue& rOp ) const
releaseDecimalPtr( aL.pDecimal );
releaseDecimalPtr( aR.pDecimal );
}
+ else if (GetType() == SbxCURRENCY && rOp.GetType() == SbxCURRENCY)
+ {
+ aL.eType = aR.eType = GetType();
+ if (Get(aL) && rOp.Get(aR))
+ bRes = CompareNormal(aL.nInt64, aR.nInt64, eOp);
+ }
// Everything else comparing on a SbxDOUBLE-Basis
else
{
@@ -1266,23 +1236,7 @@ bool SbxValue::Compare( SbxOperator eOp, const SbxValue& rOp ) const
bool bGetL = Get( aL );
bool bGetR = rOp.Get( aR );
if( bGetL && bGetR )
- switch( eOp )
- {
- case SbxEQ:
- bRes = ( aL.nDouble == aR.nDouble ); break;
- case SbxNE:
- bRes = ( aL.nDouble != aR.nDouble ); break;
- case SbxLT:
- bRes = ( aL.nDouble < aR.nDouble ); break;
- case SbxGT:
- bRes = ( aL.nDouble > aR.nDouble ); break;
- case SbxLE:
- bRes = ( aL.nDouble <= aR.nDouble ); break;
- case SbxGE:
- bRes = ( aL.nDouble >= aR.nDouble ); break;
- default:
- SetError( ERRCODE_BASIC_BAD_ARGUMENT );
- }
+ bRes = CompareNormal(aL.nDouble, aR.nDouble, eOp);
// at least one value was got
// if this is VBA then a conversion error for one
// side will yield a false result of an equality test
diff --git a/include/basic/sbxdef.hxx b/include/basic/sbxdef.hxx
index e85f1a209664..b0c667bfc854 100644
--- a/include/basic/sbxdef.hxx
+++ b/include/basic/sbxdef.hxx
@@ -194,7 +194,7 @@ constexpr sal_uInt32 SbxMAXULNG = 0xffffffff;
// Currency stored as SbxSALINT64 == sal_Int64
// value range limits are ~(2^63 - 1)/10000
// fixed precision has 4 digits right of decimal pt
-constexpr auto CURRENCY_FACTOR = 10000;
+constexpr sal_Int64 CURRENCY_FACTOR = 10000;
constexpr auto CURRENCY_FACTOR_SQUARE = 100000000;
// TODO effective MAX/MINCURR limits:
diff --git a/include/o3tl/safeint.hxx b/include/o3tl/safeint.hxx
index a9d1ef23b32b..47fdc70fd4a2 100644
--- a/include/o3tl/safeint.hxx
+++ b/include/o3tl/safeint.hxx
@@ -269,6 +269,55 @@ template<typename T> [[nodiscard]] inline T sanitizing_min(T a, T b)
return static_cast<short>(res);
}
+// A helper for taking care of signed/unsigned comparisons in constant bounds case
+// Should avoid Coverity warnings like "cid#1618764 Operands don't affect result"
+template <typename I, I Min = std::template numeric_limits<I>::min(),
+ I Max = std::template numeric_limits<I>::max(),
+ std::enable_if_t<std::is_integral_v<I> && (Min <= 0) && (Max > 0), int> = 0>
+struct ValidRange
+{
+ using SI = std::make_signed_t<I>;
+ using UI = std::make_unsigned_t<I>;
+
+ template <typename I2, std::enable_if_t<std::is_integral_v<I2>, int> = 0>
+ static constexpr bool isAbove(I2 n)
+ {
+ using UI2 = std::make_unsigned_t<I2>;
+ if constexpr (static_cast<UI2>(std::numeric_limits<I2>::max()) <= static_cast<UI>(Max))
+ return false;
+ else if constexpr (std::is_signed_v<I> == std::is_signed_v<I2>)
+ return n > Max;
+ else if constexpr (std::is_signed_v<I>) // I2 is unsigned
+ return n > static_cast<UI>(Max);
+ else // I is unsigned, I2 is signed
+ return n > 0 && static_cast<UI2>(n) > Max;
+ }
+
+ template <typename I2, std::enable_if_t<std::is_integral_v<I2>, int> = 0>
+ static constexpr bool isBelow(I2 n)
+ {
+ using SI2 = std::make_signed_t<I2>;
+ if constexpr (static_cast<SI2>(std::numeric_limits<I2>::min()) >= static_cast<SI>(Min))
+ return false; // Covers all I2 unsigned
+ else if constexpr (std::is_signed_v<I> == std::is_signed_v<I2>)
+ return n < Min;
+ else // I is unsigned, I2 is signed
+ return n < 0;
+ }
+
+ template <typename I2, std::enable_if_t<std::is_integral_v<I2>, int> = 0>
+ static constexpr bool isOutside(I2 n)
+ {
+ return isAbove(n) || isBelow(n);
+ }
+
+ template <typename I2, std::enable_if_t<std::is_integral_v<I2>, int> = 0>
+ static constexpr bool isInside(I2 n)
+ {
+ return !isOutside(n);
+ }
+};
+
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */