basic/qa/basic_coverage/test_Currency.bas |  202 ++++++++++++++++++++++++++
 basic/source/sbx/sbxbyte.cxx              |   15 -
 basic/source/sbx/sbxchar.cxx              |   17 --
 basic/source/sbx/sbxconv.hxx              |   75 +++++++++
 basic/source/sbx/sbxcurr.cxx              |  220 +++++-----------------------
 basic/source/sbx/sbxint.cxx               |   37 +---
 basic/source/sbx/sbxlng.cxx               |   12 -
 basic/source/sbx/sbxuint.cxx              |   15 -
 basic/source/sbx/sbxulng.cxx              |    2 
 basic/source/sbx/sbxvalue.cxx             |  230 ++++++++++++------------------
 include/basic/sbxdef.hxx                  |    2 
 include/o3tl/safeint.hxx                  |   49 ++++++
 12 files changed, 484 insertions(+), 392 deletions(-)

New commits:
commit b2257d1f78d7d678b87e2c3d590aba47bfec90f0
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Sun Sep 8 16:52:39 2024 +0500
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Mon Sep 9 17:46:41 2024 +0200

    Improve run-time Currency type support in Basic
    
    Change-Id: I1e04c6022944034a30ef896b8cd24050ebe3bbd5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173042
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

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: */

Reply via email to