sal/qa/rtl/math/test-rtl-math.cxx | 55 +++++++++++++++++++++++++ sal/rtl/math.cxx | 82 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+)
New commits: commit e480172cb91b4adca6a7fcd42f68c2ff2874fe54 Author: Eike Rathke <er...@redhat.com> Date: Wed Jan 13 14:40:12 2016 +0100 tdf#96918 display accurate integer double values up to (2^53)-1 (cherry picked from commit 0f6203edf74832f84d8263d7a544d679203a4efc) unit test for tdf#96918 display accurate integer double values (cherry picked from commit a3f5f4f6369552a3da870de1ed4ea9d8c628c7a8) 619e0cb0fbbfd0dfba3b2fe9c3476be55a3eea8e sal: as always C++ is too stupid to deduce parameter types of min (cherry picked from commit 2d526a9ffbad7effaabb70f4e52904c09d0ab22b) b00f29a6024e22c65a30bf4a45332e550994f03f loplugin:defaultparams (cherry picked from commit 036b4a366ecc7ea343a3fedee268463d6576cb32) Keep MSVC happy (warning C4305 when converting 9007199254740993 from __int64 to double) (cherry picked from commit e5d393cf0edc5d871cfe1aea6acede482eecec84) handle negative decimal places for rounding, tdf#96918 related (cherry picked from commit fdf982f70b2944053d995baaa3d78c7cdc4bbc4b) unit test for negative decimal places rounding, tdf#96918 related (cherry picked from commit 5299400e5cce3060a0de85bb4dedd065b5ad1f41) use getN10Exp(x) instead of pow(10.0,x) (cherry picked from commit e6c9bdfdc181c95d5757b96b6194d639a0df3c79) use ::std::swap() to reverse buffer (cherry picked from commit f19b4826c44e9e5f54b968fef59a3fcd3007d522) 0e2b92a01ba5ae1824d609ee2e557f1a1cc85cbd fa423eabc64ead519c4f4a3370a06e88ea5c7466 b2f3ffaa7c4216b66479c750465c2beab927405a b7e49126cbffc510fa941c25a8d57222bad51c46 7e18e57636416f0a3ed96c2fa3adc004fb3ba013 507a16e1d87460dead79b78621202c68ee12c2c8 Change-Id: I42001583c72bc3faab94489a4eabfa183cab5ae2 Reviewed-on: https://gerrit.libreoffice.org/21429 Tested-by: Jenkins <c...@libreoffice.org> Reviewed-by: Michael Stahl <mst...@redhat.com> diff --git a/sal/qa/rtl/math/test-rtl-math.cxx b/sal/qa/rtl/math/test-rtl-math.cxx index aa7b213..3de4956 100644 --- a/sal/qa/rtl/math/test-rtl-math.cxx +++ b/sal/qa/rtl/math/test-rtl-math.cxx @@ -88,6 +88,60 @@ public: CPPUNIT_ASSERT_EQUAL(0.0, res); } + void test_doubleToString() { + double fVal = 999999999999999.0; + sal_Int32 aGroups[3] = { 3, 2, 0 }; + rtl::OUString aRes( rtl::math::doubleToUString( fVal, + rtl_math_StringFormat_Automatic, + rtl_math_DecimalPlaces_Max, + '.', aGroups, ',', true)); + CPPUNIT_ASSERT_EQUAL( OUString("99,99,99,99,99,99,999"), aRes); + + fVal = 949.0; + aRes = rtl::math::doubleToUString( fVal, + rtl_math_StringFormat_Automatic, + -2, // round before decimals + '.', aGroups, ',', true); + CPPUNIT_ASSERT_EQUAL( OUString("900"), aRes); + + fVal = 950.0; + aRes = rtl::math::doubleToUString( fVal, + rtl_math_StringFormat_Automatic, + -2, // round before decimals + '.', aGroups, ',', true); + CPPUNIT_ASSERT_EQUAL( OUString("1,000"), aRes); + + fVal = 4503599627370495.0; + aRes = rtl::math::doubleToUString( fVal, + rtl_math_StringFormat_Automatic, + rtl_math_DecimalPlaces_Max, '.'); + CPPUNIT_ASSERT_EQUAL( OUString("4503599627370495"), aRes); + + fVal = 4503599627370496.0; + aRes = rtl::math::doubleToUString( fVal, + rtl_math_StringFormat_Automatic, + 2, '.'); + CPPUNIT_ASSERT_EQUAL( OUString("4503599627370496.00"), aRes); + + fVal = 9007199254740991.0; // (2^53)-1 + aRes = rtl::math::doubleToUString( fVal, + rtl_math_StringFormat_Automatic, + rtl_math_DecimalPlaces_Max, '.', true); + CPPUNIT_ASSERT_EQUAL( OUString("9007199254740991"), aRes); + + fVal = 9007199254740992.0; // (2^53), algorithm switch + aRes = rtl::math::doubleToUString( fVal, + rtl_math_StringFormat_Automatic, + rtl_math_DecimalPlaces_Max, '.', true); + CPPUNIT_ASSERT_EQUAL( OUString("9.00719925474099E+015"), aRes); + + fVal = 9007199254740993.0; // (2^53)+1 would be but is 9007199254740992 + aRes = rtl::math::doubleToUString( fVal, + rtl_math_StringFormat_Automatic, + rtl_math_DecimalPlaces_Max, '.', true); + CPPUNIT_ASSERT_EQUAL( OUString("9.00719925474099E+015"), aRes); + } + void test_erf() { double x, res; x = 0.0; @@ -176,6 +230,7 @@ public: CPPUNIT_TEST(test_stringToDouble_good); CPPUNIT_TEST(test_stringToDouble_bad); CPPUNIT_TEST(test_stringToDouble_exponent_without_digit); + CPPUNIT_TEST(test_doubleToString); CPPUNIT_TEST(test_erf); CPPUNIT_TEST(test_erfc); CPPUNIT_TEST(test_expm1); diff --git a/sal/rtl/math.cxx b/sal/rtl/math.cxx index fd33130..49d1544 100644 --- a/sal/rtl/math.cxx +++ b/sal/rtl/math.cxx @@ -207,6 +207,88 @@ inline void doubleToString(StringT ** pResult, return; } + // Use integer representation for integer values that fit into the + // mantissa (1.((2^53)-1)) with a precision of 1 for highest accuracy. + const sal_Int64 kMaxInt = (static_cast<sal_Int64>(1) << 53) - 1; + if ((eFormat == rtl_math_StringFormat_Automatic || + eFormat == rtl_math_StringFormat_F) && fValue <= static_cast<double>(kMaxInt)) + { + sal_Int64 nInt = static_cast<sal_Int64>(fValue); + // Check the integer range again because double comparison may yield + // true within the precision range. + if (nInt <= kMaxInt && static_cast<double>(nInt) == fValue) + { + if (nDecPlaces == rtl_math_DecimalPlaces_Max) + nDecPlaces = 0; + else + nDecPlaces = ::std::max<sal_Int32>( ::std::min<sal_Int32>( nDecPlaces, 15), -15); + if (bEraseTrailingDecZeros && nDecPlaces > 0) + nDecPlaces = 0; + + // Round before decimal position. + if (nDecPlaces < 0) + { + sal_Int64 nRounding = static_cast<sal_Int64>( getN10Exp( -nDecPlaces - 1)); + sal_Int64 nTemp = nInt / nRounding; + int nDigit = nTemp % 10; + nTemp /= 10; + if (nDigit >= 5) + ++nTemp; + nTemp *= 10; + nTemp *= nRounding; + nInt = nTemp; + nDecPlaces = 0; + } + + // Max 1 sign, 16 integer digits, 15 group separators, 1 decimal + // separator, 15 decimals digits. + typename T::Char aBuf[64]; + typename T::Char * pBuf = aBuf; + typename T::Char * p = pBuf; + + // Backward fill. + size_t nGrouping = 0; + sal_Int32 nGroupDigits = 0; + do + { + typename T::Char nDigit = nInt % 10; + nInt /= 10; + *p++ = nDigit + '0'; + if (pGroups && pGroups[nGrouping] == ++nGroupDigits && nInt > 0 && cGroupSeparator) + { + *p++ = cGroupSeparator; + if (pGroups[nGrouping+1]) + ++nGrouping; + nGroupDigits = 0; + } + } + while (nInt > 0); + if (bSign) + *p++ = '-'; + + // Reverse buffer content. + sal_Int32 n = (p - pBuf) / 2; + for (sal_Int32 i=0; i < n; ++i) + { + ::std::swap( pBuf[i], p[-i-1]); + } + // Append decimals. + if (nDecPlaces > 0) + { + *p++ = cDecSeparator; + while (nDecPlaces--) + *p++ = '0'; + } + + if (pResultCapacity == nullptr) + T::createString(pResult, pBuf, p - pBuf); + else + T::appendChars(pResult, pResultCapacity, &nResultOffset, pBuf, p - pBuf); + + return; + } + } + // find the exponent int nExp = 0; if ( fValue > 0.0 ) _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits