Related: tdf#136794 tdf#137063 Unlimit decimals in rtl_math_round()

Change-Id: I7f9fb45824458d578ee302a668386fe0c3c80974
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/116010
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Jenkins
diff --git a/sal/rtl/math.cxx b/sal/rtl/math.cxx
index a85c8ac..698e158 100644
--- a/sal/rtl/math.cxx
+++ b/sal/rtl/math.cxx
@@ -1131,8 +1131,6 @@ double SAL_CALL rtl_math_round(double fValue, int nDecPlaces,
                               enum rtl_math_RoundingMode eMode)
    SAL_THROW_EXTERN_C()
{
    OSL_ASSERT(nDecPlaces >= -20 && nDecPlaces <= 20);

    if (!std::isfinite(fValue))
        return fValue;

@@ -1142,6 +1140,8 @@ double SAL_CALL rtl_math_round(double fValue, int nDecPlaces,
    if ( nDecPlaces == 0 && eMode == rtl_math_RoundingMode_Corrected )
        return std::round( fValue );

    const double fOrigValue = fValue;

    // sign adjustment
    bool bSign = std::signbit( fValue );
    if (bSign)
@@ -1153,42 +1153,50 @@ double SAL_CALL rtl_math_round(double fValue, int nDecPlaces,
    if (nDecPlaces >= 0
            && (fValue >= (static_cast<sal_Int64>(1) << 52)
                || isRepresentableInteger(fValue)))
        return bSign ? -fValue : fValue;
        return fOrigValue;

    double fFac = 0;
    if (nDecPlaces != 0)
    {
        if (nDecPlaces > 1 && fValue > 4294967296.0)
        if (nDecPlaces > 0)
        {
            // 4294967296 is 2^32 with room for at least 20 decimals, checking
            // smaller values is not necessary. Lower the limit if more than 20
            // decimals were to be allowed.

            // Determine how many decimals are representable in the precision.
            // Anything greater 2^52 and 0.0 was already ruled out above.
            // Theoretically 0.5, 0.25, 0.125, 0.0625, 0.03125, ...
            const sal_math_Double* pd = reinterpret_cast<const sal_math_Double*>(&fValue);
            const sal_Int32 nDec = 52 - (pd->parts.exponent - 1023);

            if (nDec <= 0)
            {
                assert(!"Shouldn't this had been caught already as large number?");
                return fOrigValue;
            }

            if (nDec < nDecPlaces)
                nDecPlaces = nDec;
        }

        /* TODO: this was without the inverse factor and determining max
         * possible decimals, it could now be adjusted to be more lenient. */
        // max 20 decimals, we don't have unlimited precision
        // #38810# and no overflow on fValue*=fFac
        if (nDecPlaces < -20 || 20 < nDecPlaces || fValue > (DBL_MAX / 1e20))
            return bSign ? -fValue : fValue;

        // Avoid 1e-5 (1.0000000000000001e-05) and such inaccurate fractional
        // factors that later when dividing back spoil things. For negative
        // decimals divide first with the inverse, then multiply the rounded
        // value back.
        fFac = getN10Exp(abs(nDecPlaces));

        if (fFac == 0.0 || (nDecPlaces < 0 && !std::isfinite(fFac)))
            // Underflow, rounding to that many integer positions would be 0.
            return 0.0;

        if (!std::isfinite(fFac))
            // Overflow with very small values and high number of decimals.
            return fOrigValue;

        if (nDecPlaces < 0)
            fValue /= fFac;
        else
            fValue *= fFac;

        if (!std::isfinite(fValue))
            return fOrigValue;
    }

    // Round only if not already in distance precision gaps of integers, where
@@ -1276,6 +1284,9 @@ double SAL_CALL rtl_math_round(double fValue, int nDecPlaces,
            fValue /= fFac;
    }

    if (!std::isfinite(fValue))
        return fOrigValue;

    return bSign ? -fValue : fValue;
}