diff options
author | Eike Rathke <erack@redhat.com> | 2020-12-17 20:44:43 +0100 |
---|---|---|
committer | Eike Rathke <erack@redhat.com> | 2020-12-18 11:00:57 +0100 |
commit | ead1cdd23f5379f5cd8f69d5e73f410a67896db2 (patch) | |
tree | 77397c0a7b5b3fc16ad4ae6da3d24eefd7ffdee5 /sal | |
parent | 6b161f615712f6daa11f66d0dfe45eef0877c860 (diff) |
Check intermediate for not to be rounded value, tdf#138360 follow-up
This is a combination of 3 commits.
Add sal_uInt64 fields to sal_math_Double
We may need them later, and at least don't have a confusing
inf_parts or nan_parts struct name but just parts as well.
Ife0cf279c47d2815aa2a1483223397b147e9d776
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107924
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Jenkins
Replace log2() call with parts.exponent-1023, tdf#138360 follow-up
... to save some cycles as we anyway need only the integer value
of the exponent and even exactly this value for the number of
possible decimals.
I8d462f53cadde6a95d57d1342d8487fbfa001ae9
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107928
Tested-by: Jenkins
Reviewed-by: Eike Rathke <erack@redhat.com>
Check intermediate for not to be rounded value, tdf#138360 follow-up
I98cc25267e7a10c34179bab50d19f49436e1c48c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107929
Tested-by: Jenkins
Reviewed-by: Eike Rathke <erack@redhat.com>
Change-Id: I98cc25267e7a10c34179bab50d19f49436e1c48c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107931
Tested-by: Jenkins
Reviewed-by: Eike Rathke <erack@redhat.com>
Diffstat (limited to 'sal')
-rw-r--r-- | sal/rtl/math.cxx | 135 |
1 files changed, 71 insertions, 64 deletions
diff --git a/sal/rtl/math.cxx b/sal/rtl/math.cxx index 46a3e925b95b..a85c8ac6e959 100644 --- a/sal/rtl/math.cxx +++ b/sal/rtl/math.cxx @@ -1167,9 +1167,10 @@ double SAL_CALL rtl_math_round(double fValue, int nDecPlaces, // 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 double fDec = 52 - log2(fValue) + 1; - if (fDec < nDecPlaces) - nDecPlaces = static_cast<sal_Int32>(fDec); + const sal_math_Double* pd = reinterpret_cast<const sal_math_Double*>(&fValue); + const sal_Int32 nDec = 52 - (pd->parts.exponent - 1023); + if (nDec < nDecPlaces) + nDecPlaces = nDec; } /* TODO: this was without the inverse factor and determining max @@ -1190,75 +1191,81 @@ double SAL_CALL rtl_math_round(double fValue, int nDecPlaces, fValue *= fFac; } - switch ( eMode ) + // Round only if not already in distance precision gaps of integers, where + // for [2^52,2^53) adding 0.5 would even yield the next representable + // integer. + if (fValue < (static_cast<sal_Int64>(1) << 52)) { - case rtl_math_RoundingMode_Corrected : - fValue = rtl::math::approxFloor(fValue + 0.5); - break; - case rtl_math_RoundingMode_Down: - fValue = rtl::math::approxFloor(fValue); - break; - case rtl_math_RoundingMode_Up: - fValue = rtl::math::approxCeil(fValue); - break; - case rtl_math_RoundingMode_Floor: - fValue = bSign ? rtl::math::approxCeil(fValue) - : rtl::math::approxFloor( fValue ); - break; - case rtl_math_RoundingMode_Ceiling: - fValue = bSign ? rtl::math::approxFloor(fValue) - : rtl::math::approxCeil(fValue); - break; - case rtl_math_RoundingMode_HalfDown : - { - double f = floor(fValue); - fValue = ((fValue - f) <= 0.5) ? f : ceil(fValue); - } - break; - case rtl_math_RoundingMode_HalfUp: + switch ( eMode ) { - double f = floor(fValue); - fValue = ((fValue - f) < 0.5) ? f : ceil(fValue); - } - break; - case rtl_math_RoundingMode_HalfEven: + case rtl_math_RoundingMode_Corrected : + fValue = rtl::math::approxFloor(fValue + 0.5); + break; + case rtl_math_RoundingMode_Down: + fValue = rtl::math::approxFloor(fValue); + break; + case rtl_math_RoundingMode_Up: + fValue = rtl::math::approxCeil(fValue); + break; + case rtl_math_RoundingMode_Floor: + fValue = bSign ? rtl::math::approxCeil(fValue) + : rtl::math::approxFloor( fValue ); + break; + case rtl_math_RoundingMode_Ceiling: + fValue = bSign ? rtl::math::approxFloor(fValue) + : rtl::math::approxCeil(fValue); + break; + case rtl_math_RoundingMode_HalfDown : + { + double f = floor(fValue); + fValue = ((fValue - f) <= 0.5) ? f : ceil(fValue); + } + break; + case rtl_math_RoundingMode_HalfUp: + { + double f = floor(fValue); + fValue = ((fValue - f) < 0.5) ? f : ceil(fValue); + } + break; + case rtl_math_RoundingMode_HalfEven: #if defined FLT_ROUNDS -/* - Use fast version. FLT_ROUNDS may be defined to a function by some compilers! - - DBL_EPSILON is the smallest fractional number which can be represented, - its reciprocal is therefore the smallest number that cannot have a - fractional part. Once you add this reciprocal to `x', its fractional part - is stripped off. Simply subtracting the reciprocal back out returns `x' - without its fractional component. - Simple, clever, and elegant - thanks to Ross Cottrell, the original author, - who placed it into public domain. - - volatile: prevent compiler from being too smart -*/ - if (FLT_ROUNDS == 1) - { - volatile double x = fValue + 1.0 / DBL_EPSILON; - fValue = x - 1.0 / DBL_EPSILON; - } - else -#endif // FLT_ROUNDS - { - double f = floor(fValue); - if ((fValue - f) != 0.5) + /* + Use fast version. FLT_ROUNDS may be defined to a function by some compilers! + + DBL_EPSILON is the smallest fractional number which can be represented, + its reciprocal is therefore the smallest number that cannot have a + fractional part. Once you add this reciprocal to `x', its fractional part + is stripped off. Simply subtracting the reciprocal back out returns `x' + without its fractional component. + Simple, clever, and elegant - thanks to Ross Cottrell, the original author, + who placed it into public domain. + + volatile: prevent compiler from being too smart + */ + if (FLT_ROUNDS == 1) { - fValue = floor( fValue + 0.5 ); + volatile double x = fValue + 1.0 / DBL_EPSILON; + fValue = x - 1.0 / DBL_EPSILON; } else +#endif // FLT_ROUNDS { - double g = f / 2.0; - fValue = (g == floor( g )) ? f : (f + 1.0); + double f = floor(fValue); + if ((fValue - f) != 0.5) + { + fValue = floor( fValue + 0.5 ); + } + else + { + double g = f / 2.0; + fValue = (g == floor( g )) ? f : (f + 1.0); + } } - } - break; - default: - OSL_ASSERT(false); - break; + break; + default: + OSL_ASSERT(false); + break; + } } if (nDecPlaces != 0) |