diff options
-rw-r--r-- | basic/qa/basic_coverage/test_Currency.bas | 202 | ||||
-rw-r--r-- | basic/source/sbx/sbxbyte.cxx | 15 | ||||
-rw-r--r-- | basic/source/sbx/sbxchar.cxx | 17 | ||||
-rw-r--r-- | basic/source/sbx/sbxconv.hxx | 75 | ||||
-rw-r--r-- | basic/source/sbx/sbxcurr.cxx | 220 | ||||
-rw-r--r-- | basic/source/sbx/sbxint.cxx | 37 | ||||
-rw-r--r-- | basic/source/sbx/sbxlng.cxx | 12 | ||||
-rw-r--r-- | basic/source/sbx/sbxuint.cxx | 15 | ||||
-rw-r--r-- | basic/source/sbx/sbxulng.cxx | 2 | ||||
-rw-r--r-- | basic/source/sbx/sbxvalue.cxx | 230 | ||||
-rw-r--r-- | include/basic/sbxdef.hxx | 2 | ||||
-rw-r--r-- | include/o3tl/safeint.hxx | 49 |
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: */ |