Use Dragonbox to implement doubleTo*String*
This header-only library is accurate in decimal representation of doubles; provides API that allows to create custom representation - so it's possible to use custom decimal separators and grouping. This allows to unify all corner cases: integers, numbers close to DBL_MAX, up-rounding to the next decade. Note that Dragonbox creates the shortest decimal representation of the number, that is unambiguously convertible back to the same number; thus it may hide trailing digits that are unneeded for such conversion. The functional changes are minimal, and beneficial: 1. Rounding numbers close to DBL_MAX now takes into account the bEraseTrailingDecZeros argument, as it should, allowing to have "1.8E+308" for rounding DBL_MAX to 2 decimals without trailing zeroes, instead of previous "1.80E+308". 2. Incorrect rounding is fixed in some cases, e.g. 9.9999999999999929 rounded to 10 previously using rtl_math_DecimalPlaces_Max. 3. Representing the number in the shortest way may change display of some printed numbers. E.g., 5th greatest double is represented as "1.797693134862315E+308" instead of a bit longer, but giving the same double on roundtrip, "1.7976931348623149E+308". This would generally look better for some numbers similar to the famous 0.1, where users would likely expect more "round" representation where it's unambiguous (but we still truncate to 15 significant decimals anyway - so there's no point in pretending to provide exact digits for actual binary representation). These are reflected in the unit tests affected by the change. Change-Id: I05e20274a30eec499593ee3e9ec070e1269232a2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129948 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
This commit is contained in:
@@ -110,6 +110,7 @@ $(WORKDIR)/download: $(BUILDDIR)/config_$(gb_Side).mk $(SRCDIR)/download.lst $(S
|
||||
$(call fetch_Optional,CAIRO,PIXMAN_TARBALL) \
|
||||
$(call fetch_Optional,CDR,CDR_TARBALL) \
|
||||
$(call fetch_Optional,CLUCENE,CLUCENE_TARBALL) \
|
||||
DRAGONBOX_TARBALL \
|
||||
DTOA_TARBALL \
|
||||
$(call fetch_Optional,LIBCMIS,LIBCMIS_TARBALL) \
|
||||
$(call fetch_Optional,COINMP,COINMP_TARBALL) \
|
||||
|
||||
@@ -4132,6 +4132,15 @@ $(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,ooo,\
|
||||
))
|
||||
endif
|
||||
|
||||
define gb_LinkTarget__use_dragonbox
|
||||
$(call gb_LinkTarget_use_unpacked,$(1),dragonbox)
|
||||
$(call gb_LinkTarget_set_include,$(1),\
|
||||
-I$(call gb_UnpackedTarball_get_dir,dragonbox/include/)\
|
||||
$$(INCLUDE) \
|
||||
)
|
||||
|
||||
endef
|
||||
|
||||
define gb_LinkTarget__use_dtoa
|
||||
$(call gb_LinkTarget_use_unpacked,$(1),dtoa)
|
||||
$(call gb_LinkTarget_set_include,$(1),\
|
||||
|
||||
@@ -23,6 +23,8 @@ export CDR_SHA256SUM := 5666249d613466b9aa1e987ea4109c04365866e9277d80f6cd9663e8
|
||||
export CDR_TARBALL := libcdr-0.1.7.tar.xz
|
||||
export CLUCENE_SHA256SUM := ddfdc433dd8ad31b5c5819cc4404a8d2127472a3b720d3e744e8c51d79732eab
|
||||
export CLUCENE_TARBALL := 48d647fbd8ef8889e5a7f422c1bfda94-clucene-core-2.3.3.4.tar.gz
|
||||
export DRAGONBOX_SHA256SUM := 293247ccba995ec47ae3abb52c3e83904a7d71efb7093d4c0d2c6367c1cc1e20
|
||||
export DRAGONBOX_TARBALL := dragonbox-1.1.0.tar.gz
|
||||
export DTOA_SHA256SUM := 0082d0684f7db6f62361b76c4b7faba19e0c7ce5cb8e36c4b65fea8281e711b4
|
||||
export DTOA_TARBALL := dtoa-20180411.tgz
|
||||
export LIBCMIS_SHA256SUM := d7b18d9602190e10d437f8a964a32e983afd57e2db316a07d87477a79f5000a2
|
||||
|
||||
Vendored
+1
@@ -30,6 +30,7 @@ $(eval $(call gb_Module_add_moduledirs,external,\
|
||||
$(call gb_Helper_optional,CPPUNIT,cppunit) \
|
||||
$(call gb_Helper_optional,CT2N,ct2n) \
|
||||
$(call gb_Helper_optional,CURL,curl) \
|
||||
dragonbox \
|
||||
dtoa \
|
||||
$(call gb_Helper_optional,EBOOK,libebook) \
|
||||
$(call gb_Helper_optional,EPM,epm) \
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 100 -*-
|
||||
#
|
||||
# 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/.
|
||||
#
|
||||
|
||||
$(eval $(call gb_Module_Module,dragonbox))
|
||||
|
||||
$(eval $(call gb_Module_add_targets,dragonbox,\
|
||||
UnpackedTarball_dragonbox \
|
||||
))
|
||||
|
||||
# vim: set noet sw=4 ts=4:
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
Dragonbox is available from [ https://github.com/jk-jeon/dragonbox ].
|
||||
|
||||
Used to convert a double to a decimal string, providing breakout of significand and exponent
|
||||
as integers, allowing to create custom number representation (e.g., locale-specific).
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 100 -*-
|
||||
#
|
||||
# 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/.
|
||||
#
|
||||
|
||||
$(eval $(call gb_UnpackedTarball_UnpackedTarball,dragonbox))
|
||||
|
||||
$(eval $(call gb_UnpackedTarball_set_tarball,dragonbox,$(DRAGONBOX_TARBALL)))
|
||||
|
||||
# vim: set noet sw=4 ts=4:
|
||||
@@ -1844,6 +1844,27 @@
|
||||
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
||||
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.</p>
|
||||
</div>
|
||||
<h2>Dragonbox</h2>
|
||||
<p>The following software may be included in this product: Dragonbox.</p>
|
||||
<p>Dragonbox code is covered by the following license:</p>
|
||||
<p><a href="#a__Apache_License_version_2_0">Jump to Apache License Version 2.0</a></p>
|
||||
<blockquote>
|
||||
<p>--- LLVM Exceptions to the Apache 2.0 License ----</p>
|
||||
|
||||
<p>As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into an Object form of such source code, you
|
||||
may redistribute such embedded portions in such Object form without complying
|
||||
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.</p>
|
||||
|
||||
<p>In addition, if you combine or link compiled forms of this Software with
|
||||
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||
court of competent jurisdiction determines that the patent provision (Section
|
||||
3), the indemnity provision (Section 9) or other Section of the License
|
||||
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||
the License, but only in their entirety and only with respect to the Combined
|
||||
Software.</p>
|
||||
</blockquote>
|
||||
<h2>dtoa</h2>
|
||||
<p>The following software may be included in this product: dtoa.</p>
|
||||
<p>dtoa code is covered by the MIT license:</p>
|
||||
|
||||
@@ -43,6 +43,7 @@ $(eval $(call gb_Library_use_libraries,sal,\
|
||||
))
|
||||
|
||||
$(eval $(call gb_Library_use_externals,sal,\
|
||||
dragonbox \
|
||||
dtoa \
|
||||
valgrind \
|
||||
zlib \
|
||||
|
||||
@@ -432,7 +432,8 @@ public:
|
||||
fVal = std::nextafter( fVal, 0);
|
||||
aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_Automatic,
|
||||
rtl_math_DecimalPlaces_Max, '.', true);
|
||||
CPPUNIT_ASSERT_EQUAL( OUString("1.7976931348623149E+308"), aRes);
|
||||
CPPUNIT_ASSERT_EQUAL( OUString("1.797693134862315E+308"), aRes);
|
||||
CPPUNIT_ASSERT_EQUAL(fVal, rtl::math::stringToDouble(aRes, '.', ',')); // Test roundtrip
|
||||
|
||||
// DBL_MAX and 4 nextafters rounded to 15 decimals
|
||||
fVal = DBL_MAX;
|
||||
@@ -463,24 +464,20 @@ public:
|
||||
// DBL_MAX rounded to 2 decimals
|
||||
fVal = DBL_MAX;
|
||||
aRes = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_Automatic, 2, '.', true);
|
||||
CPPUNIT_ASSERT_EQUAL( OUString("1.80E+308"), aRes);
|
||||
CPPUNIT_ASSERT_EQUAL( OUString("1.8E+308"), aRes);
|
||||
|
||||
// Crashed after commit eae24a9488814e77254d175c11fc4a138c1dbd30
|
||||
fVal = 123456.789;
|
||||
aRes = rtl::math::doubleToUString(fVal, rtl_math_StringFormat_E, 2, '.', false);
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("1.23E+005"), aRes);
|
||||
|
||||
// Testing "after-treatment of up-rounding to the next decade" branch
|
||||
// See void doubleToString in sal/rtl/math.cxx
|
||||
// 1. Yet empty buffer
|
||||
fVal = 9.9999999999999929;
|
||||
aRes = rtl::math::doubleToUString(fVal, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true);
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("10"), aRes);
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("9.99999999999999"), aRes);
|
||||
|
||||
// 2. Buffer with some content
|
||||
fVal = 0.99999999999999933;
|
||||
aRes = rtl::math::doubleToUString(fVal, rtl_math_StringFormat_F, rtl_math_DecimalPlaces_Max, '.', true);
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("1"), aRes);
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("0.999999999999999"), aRes);
|
||||
}
|
||||
|
||||
void test_approx() {
|
||||
|
||||
+71
-217
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <rtl/math.h>
|
||||
|
||||
#include <o3tl/safeint.hxx>
|
||||
#include <osl/diagnose.h>
|
||||
#include <rtl/character.hxx>
|
||||
#include <rtl/math.hxx>
|
||||
@@ -34,6 +35,7 @@
|
||||
#include <memory>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <dragonbox/dragonbox.h>
|
||||
#include <dtoa.h>
|
||||
|
||||
constexpr int minExp = -323, maxExp = 308;
|
||||
@@ -104,11 +106,6 @@ static double getN10Exp(int nExp)
|
||||
|
||||
namespace {
|
||||
|
||||
double const nCorrVal[] = {
|
||||
0, 9e-1, 9e-2, 9e-3, 9e-4, 9e-5, 9e-6, 9e-7, 9e-8,
|
||||
9e-9, 9e-10, 9e-11, 9e-12, 9e-13, 9e-14, 9e-15
|
||||
};
|
||||
|
||||
struct StringTraits
|
||||
{
|
||||
typedef char Char;
|
||||
@@ -245,6 +242,38 @@ int getBitsInFracPart(double fAbsValue)
|
||||
return std::max(nBitsInFracPart, 0);
|
||||
}
|
||||
|
||||
constexpr sal_uInt64 eX[] = { 10ull,
|
||||
100ull,
|
||||
1000ull,
|
||||
10000ull,
|
||||
100000ull,
|
||||
1000000ull,
|
||||
10000000ull,
|
||||
100000000ull,
|
||||
1000000000ull,
|
||||
10000000000ull,
|
||||
100000000000ull,
|
||||
1000000000000ull,
|
||||
10000000000000ull,
|
||||
100000000000000ull,
|
||||
1000000000000000ull,
|
||||
10000000000000000ull,
|
||||
100000000000000000ull,
|
||||
1000000000000000000ull,
|
||||
10000000000000000000ull };
|
||||
|
||||
int decimalDigits(sal_uInt64 n)
|
||||
{
|
||||
return std::distance(std::begin(eX), std::upper_bound(std::begin(eX), std::end(eX), n)) + 1;
|
||||
}
|
||||
|
||||
sal_uInt64 roundToPow10(sal_uInt64 n, int e)
|
||||
{
|
||||
assert(e > 0 && o3tl::make_unsigned(e) <= std::size(eX));
|
||||
const sal_uInt64 d = eX[e - 1];
|
||||
return (n + d / 2) / d * d;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
void doubleToString(typename T::String ** pResult,
|
||||
sal_Int32 * pResultCapacity, sal_Int32 nResultOffset,
|
||||
@@ -254,18 +283,6 @@ void doubleToString(typename T::String ** pResult,
|
||||
typename T::Char cGroupSeparator,
|
||||
bool bEraseTrailingDecZeros)
|
||||
{
|
||||
static double const nRoundVal[] = {
|
||||
5.0e+0, 0.5e+0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6,
|
||||
0.5e-7, 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14
|
||||
};
|
||||
|
||||
// sign adjustment, instead of testing for fValue<0.0 this will also fetch
|
||||
// -0.0
|
||||
bool bSign = std::signbit(fValue);
|
||||
|
||||
if (bSign)
|
||||
fValue = -fValue;
|
||||
|
||||
if (std::isnan(fValue))
|
||||
{
|
||||
// #i112652# XMLSchema-2
|
||||
@@ -283,8 +300,7 @@ void doubleToString(typename T::String ** pResult,
|
||||
return;
|
||||
}
|
||||
|
||||
bool bHuge = fValue == HUGE_VAL; // g++ 3.0.1 requires it this way...
|
||||
if (bHuge || std::isinf(fValue))
|
||||
if (std::isinf(fValue))
|
||||
{
|
||||
// #i112652# XMLSchema-2
|
||||
sal_Int32 nCapacity = RTL_CONSTASCII_LENGTH("-INF");
|
||||
@@ -295,7 +311,7 @@ void doubleToString(typename T::String ** pResult,
|
||||
nResultOffset = 0;
|
||||
}
|
||||
|
||||
if ( bSign )
|
||||
if (std::signbit(fValue))
|
||||
T::appendAscii(pResult, pResultCapacity, &nResultOffset,
|
||||
RTL_CONSTASCII_STRINGPARAM("-"));
|
||||
|
||||
@@ -305,6 +321,18 @@ void doubleToString(typename T::String ** pResult,
|
||||
return;
|
||||
}
|
||||
|
||||
decltype(jkj::dragonbox::to_decimal(fValue)) aParts{};
|
||||
if (fValue) // to_decimal is documented to only handle non-zero finite numbers
|
||||
aParts = jkj::dragonbox::to_decimal(fValue);
|
||||
else
|
||||
aParts.is_negative = std::signbit(fValue); // Handle -0.0
|
||||
|
||||
int nOrigDigits = decimalDigits(aParts.significand);
|
||||
int nExp = nOrigDigits + aParts.exponent - 1;
|
||||
int nRoundDigits = 15;
|
||||
if (aParts.is_negative)
|
||||
fValue = -fValue;
|
||||
|
||||
// Unfortunately the old rounding below writes 1.79769313486232e+308 for
|
||||
// DBL_MAX and 4 subsequent nextafter(...,0).
|
||||
static const double fB1 = std::nextafter( DBL_MAX, 0);
|
||||
@@ -318,77 +346,17 @@ void doubleToString(typename T::String ** pResult,
|
||||
// they exceed range they should not be written to exchange strings or
|
||||
// file formats.
|
||||
|
||||
// Writing pDig up to decimals(-1,-2) then appending one digit from
|
||||
// pRou xor one or two digits from pSlot[].
|
||||
constexpr char pDig[] = "7976931348623157";
|
||||
constexpr char pRou[] = "8087931359623267"; // the only up-carry is 80
|
||||
static_assert(SAL_N_ELEMENTS(pDig) == SAL_N_ELEMENTS(pRou), "digit count mismatch");
|
||||
constexpr sal_Int32 nDig2 = RTL_CONSTASCII_LENGTH(pRou) - 2;
|
||||
sal_Int32 nCapacity = RTL_CONSTASCII_LENGTH(pRou) + 8; // + "-1.E+308"
|
||||
const char pSlot[5][2][3] =
|
||||
{ // rounded, not
|
||||
"67", "57", // DBL_MAX
|
||||
"65", "55",
|
||||
"53", "53",
|
||||
"51", "51",
|
||||
"59", "49",
|
||||
};
|
||||
|
||||
if (!pResultCapacity)
|
||||
{
|
||||
pResultCapacity = &nCapacity;
|
||||
T::createBuffer(pResult, pResultCapacity);
|
||||
nResultOffset = 0;
|
||||
}
|
||||
|
||||
if (bSign)
|
||||
T::appendAscii(pResult, pResultCapacity, &nResultOffset,
|
||||
RTL_CONSTASCII_STRINGPARAM("-"));
|
||||
|
||||
nDecPlaces = std::clamp<sal_Int32>( nDecPlaces, 0, RTL_CONSTASCII_LENGTH(pRou));
|
||||
if (nDecPlaces == 0)
|
||||
{
|
||||
T::appendAscii(pResult, pResultCapacity, &nResultOffset,
|
||||
RTL_CONSTASCII_STRINGPARAM("2"));
|
||||
}
|
||||
else
|
||||
{
|
||||
T::appendAscii(pResult, pResultCapacity, &nResultOffset,
|
||||
RTL_CONSTASCII_STRINGPARAM("1"));
|
||||
T::appendChars(pResult, pResultCapacity, &nResultOffset, &cDecSeparator, 1);
|
||||
if (nDecPlaces <= 2)
|
||||
{
|
||||
T::appendAscii(pResult, pResultCapacity, &nResultOffset, pRou, nDecPlaces);
|
||||
}
|
||||
else if (nDecPlaces <= nDig2)
|
||||
{
|
||||
T::appendAscii(pResult, pResultCapacity, &nResultOffset, pDig, nDecPlaces - 1);
|
||||
T::appendAscii(pResult, pResultCapacity, &nResultOffset, pRou + nDecPlaces - 1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
const sal_Int32 nDec = nDecPlaces - nDig2;
|
||||
nDecPlaces -= nDec;
|
||||
// nDec-1 is also offset into slot, rounded(1-1=0) or not(2-1=1)
|
||||
const size_t nSlot = ((fValue < fB3) ? 4 : ((fValue < fB2) ? 3
|
||||
: ((fValue < fB1) ? 2 : ((fValue < DBL_MAX) ? 1 : 0))));
|
||||
|
||||
T::appendAscii(pResult, pResultCapacity, &nResultOffset, pDig, nDecPlaces);
|
||||
T::appendAscii(pResult, pResultCapacity, &nResultOffset, pSlot[nSlot][nDec-1], nDec);
|
||||
}
|
||||
}
|
||||
T::appendAscii(pResult, pResultCapacity, &nResultOffset,
|
||||
RTL_CONSTASCII_STRINGPARAM("E+308"));
|
||||
|
||||
return;
|
||||
eFormat = rtl_math_StringFormat_E;
|
||||
nDecPlaces = std::clamp<sal_Int32>( nDecPlaces, 0, 16);
|
||||
nRoundDigits = 17;
|
||||
}
|
||||
|
||||
// Use integer representation for integer values that fit into the
|
||||
// mantissa (1.((2^53)-1)) with a precision of 1 for highest accuracy.
|
||||
if ((eFormat == rtl_math_StringFormat_Automatic ||
|
||||
eFormat == rtl_math_StringFormat_F) && isRepresentableInteger(fValue))
|
||||
eFormat == rtl_math_StringFormat_F) && aParts.exponent >= 0 && fValue < 0x1p53)
|
||||
{
|
||||
sal_Int64 nInt = static_cast< sal_Int64 >(fValue);
|
||||
eFormat = rtl_math_StringFormat_F;
|
||||
if (nDecPlaces == rtl_math_DecimalPlaces_Max)
|
||||
nDecPlaces = 0;
|
||||
else
|
||||
@@ -397,68 +365,7 @@ void doubleToString(typename T::String ** pResult,
|
||||
if (bEraseTrailingDecZeros && nDecPlaces > 0)
|
||||
nDecPlaces = 0;
|
||||
|
||||
// Round before decimal position.
|
||||
if (nDecPlaces < 0)
|
||||
{
|
||||
sal_Int64 nRounding = static_cast< sal_Int64 >(getN10Exp(-nDecPlaces - 1));
|
||||
const sal_Int64 nTemp = (nInt / nRounding + 5) / 10;
|
||||
nInt = nTemp * 10 * nRounding;
|
||||
}
|
||||
|
||||
// Max 1 sign, 16 integer digits, 15 group separators, 1 decimal
|
||||
// separator, 15 decimals digits.
|
||||
typename T::Char aBuf[64];
|
||||
typename T::Char* pEnd = aBuf + 40;
|
||||
typename T::Char* pStart = pEnd;
|
||||
|
||||
// Backward fill.
|
||||
sal_Int32 nGrouping = cGroupSeparator && pGroups ? *pGroups : 0;
|
||||
sal_Int32 nGroupDigits = 0;
|
||||
do
|
||||
{
|
||||
typename T::Char nDigit = nInt % 10;
|
||||
nInt /= 10;
|
||||
*--pStart = nDigit + '0';
|
||||
if (nGrouping && nGrouping == ++nGroupDigits && nInt)
|
||||
{
|
||||
*--pStart = cGroupSeparator;
|
||||
if (*(pGroups + 1))
|
||||
nGrouping = *++pGroups;
|
||||
nGroupDigits = 0;
|
||||
}
|
||||
}
|
||||
while (nInt);
|
||||
if (bSign)
|
||||
*--pStart = '-';
|
||||
|
||||
// Append decimals.
|
||||
if (nDecPlaces > 0)
|
||||
{
|
||||
*pEnd++ = cDecSeparator;
|
||||
pEnd = std::fill_n(pEnd, nDecPlaces, '0');
|
||||
}
|
||||
|
||||
if (!pResultCapacity)
|
||||
T::createString(pResult, pStart, pEnd - pStart);
|
||||
else
|
||||
T::appendChars(pResult, pResultCapacity, &nResultOffset, pStart, pEnd - pStart);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// find the exponent
|
||||
int nExp = 0;
|
||||
if ( fValue > 0.0 )
|
||||
{
|
||||
// Cap nExp at a small value beyond which "fValue /= N10Exp" would lose precision (or N10Exp
|
||||
// might even be zero); that will produce output with the decimal point in a non-normalized
|
||||
// position, but the current quality of output for such small values is probably abysmal,
|
||||
// anyway:
|
||||
nExp = std::max(
|
||||
static_cast< int >(floor(log10(fValue))), std::numeric_limits<double>::min_exponent10);
|
||||
double const N10Exp = getN10Exp(nExp);
|
||||
assert(N10Exp != 0);
|
||||
fValue /= N10Exp;
|
||||
nRoundDigits = nOrigDigits; // no rounding
|
||||
}
|
||||
|
||||
switch (eFormat)
|
||||
@@ -531,12 +438,14 @@ void doubleToString(typename T::String ** pResult,
|
||||
nDigits += nExp;
|
||||
|
||||
// Round the number
|
||||
if(nDigits >= 0)
|
||||
nRoundDigits = std::min<int>(nDigits, nRoundDigits);
|
||||
if(nDigits >= 0 && nOrigDigits > nRoundDigits)
|
||||
{
|
||||
fValue += nRoundVal[std::min<sal_Int32>(nDigits, 15)];
|
||||
if (fValue >= 10)
|
||||
aParts.significand = roundToPow10(aParts.significand, nOrigDigits - nRoundDigits);
|
||||
assert(aParts.significand <= eX[nOrigDigits - 1]);
|
||||
if (aParts.significand == eX[nOrigDigits - 1]) // up-rounding to the next decade
|
||||
{
|
||||
fValue = 1.0;
|
||||
nOrigDigits++;
|
||||
nExp++;
|
||||
|
||||
if (eFormat == rtl_math_StringFormat_F)
|
||||
@@ -552,7 +461,7 @@ void doubleToString(typename T::String ** pResult,
|
||||
assert(nBuf <= 1024);
|
||||
typename T::Char* pBuf = static_cast<typename T::Char*>(alloca(nBuf * sizeof(typename T::Char)));
|
||||
typename T::Char * p = pBuf;
|
||||
if ( bSign )
|
||||
if (aParts.is_negative)
|
||||
*p++ = '-';
|
||||
|
||||
bool bHasDec = false;
|
||||
@@ -612,77 +521,22 @@ void doubleToString(typename T::String ** pResult,
|
||||
// print the number
|
||||
if (nDigits > 0)
|
||||
{
|
||||
for (int i = 0; ; i++)
|
||||
for (int nCurExp = nOrigDigits - 1;;)
|
||||
{
|
||||
if (i < 15) // was 16 in ancient versions, which leads to inaccuracies
|
||||
int nDigit;
|
||||
if (aParts.significand > 0 && nCurExp > 0)
|
||||
{
|
||||
int nDigit;
|
||||
if (nDigits-1 == 0 && i > 0 && i < 14)
|
||||
nDigit = floor( fValue + nCorrVal[15-i]);
|
||||
else
|
||||
nDigit = fValue + 1E-15;
|
||||
|
||||
if (nDigit >= 10)
|
||||
{ // after-treatment of up-rounding to the next decade
|
||||
typename T::Char* p1 = pBuf;
|
||||
// Assert that no one changed the logic we rely on.
|
||||
assert(!bSign || *p1 == '-');
|
||||
// Do not touch leading minus sign put earlier.
|
||||
if (bSign)
|
||||
++p1;
|
||||
assert(p1 <= p);
|
||||
if (p1 == p)
|
||||
{
|
||||
*p++ = '1';
|
||||
if (eFormat != rtl_math_StringFormat_F)
|
||||
{
|
||||
*p++ = cDecSeparator;
|
||||
nExp++;
|
||||
bHasDec = true;
|
||||
}
|
||||
*p++ = '0';
|
||||
}
|
||||
else
|
||||
{
|
||||
for (typename T::Char* p2 = p - 1; p2 >= p1; --p2)
|
||||
{
|
||||
typename T::Char cS = *p2;
|
||||
if (cS == cDecSeparator)
|
||||
continue;
|
||||
if (cS != '9')
|
||||
{
|
||||
++*p2;
|
||||
break;
|
||||
}
|
||||
*p2 = '0';
|
||||
if (p2 == p1) // The number consisted of all 9s replaced to all 0s
|
||||
{
|
||||
if (eFormat == rtl_math_StringFormat_F)
|
||||
{ // move everything to the right before inserting '1'
|
||||
std::memmove(p2 + 1, p2, (p++ - p2) * sizeof(*p));
|
||||
}
|
||||
else
|
||||
{
|
||||
nExp++;
|
||||
}
|
||||
*p2 = '1';
|
||||
}
|
||||
}
|
||||
|
||||
*p++ = '0';
|
||||
}
|
||||
fValue = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*p++ = nDigit + '0';
|
||||
fValue = (fValue - nDigit) * 10.0;
|
||||
}
|
||||
--nCurExp;
|
||||
nDigit = aParts.significand / eX[nCurExp];
|
||||
aParts.significand %= eX[nCurExp];
|
||||
}
|
||||
else
|
||||
{
|
||||
*p++ = '0';
|
||||
nDigit = aParts.significand;
|
||||
aParts.significand = 0;
|
||||
}
|
||||
assert(nDigit >= 0 && nDigit < 10);
|
||||
*p++ = nDigit + '0';
|
||||
|
||||
if (!--nDigits)
|
||||
break; // for
|
||||
|
||||
@@ -1632,7 +1632,7 @@ void SdOOXMLExportTest2::testTdf118825()
|
||||
xmlDocUniquePtr pXmlDocContent = parseExport(tempFile, "ppt/slides/slide1.xml");
|
||||
|
||||
|
||||
CPPUNIT_ASSERT_MOTIONPATH("M 0.0449285714285714 0.00368253968253968 C 0.0575714285714285 -0.00095238095238096 0.0704264795523803 -0.00370117418637049 0.0831071428571428 -0.00819047619047622 C 0.0953550597998766 -0.0125265741339082 0.107821870086751 -0.010397536991717 0.120321428571429 -0.0115555555555556 C 0.133179018681433 -0.0127467438724762 0.151318627483861 -0.0158700272533852 0.1585 0.00539682539682542 C 0.16478291361998 0.0240029898688431 0.15828642886492 0.0483806254341085 0.161392857142857 0.0698412698412698 C 0.165179286017685 0.0959996731216037 0.17453898927982 0.119735912694626 0.187142857142857 0.132634920634921 C 0.199788991845377 0.145577185161529 0.215607110490848 0.142889773028431 0.230107142857143 0.142857142857143 C 0.243821417584191 0.142826280916829 0.257716514999779 0.142685979556724 0.271142857142857 0.137777777777778 C 0.286895094567923 0.132019309914514 0.302318190711873 0.122962218306185 0.317928571428571 0.11568253968254 C 0.333496771884547 0.108422531222479 0.348787823719556 0.0990570571890929 0.363714285714286 0.0885079365079364 C 0.374930683062651 0.080580865157908 0.385357142857143 0.0693333333333332 0.396178571428571 0.0596825396825396 L 0.404785714285714 0.0410158730158729 L 0.401892857142857 0.0342222222222221 E", getXPath(pXmlDocContent, "(//p:animMotion)[1]", "path"));
|
||||
CPPUNIT_ASSERT_MOTIONPATH("M 0.0449285714285714 0.00368253968253968 C 0.0575714285714285 -0.00095238095238096 0.0704264795523803 -0.00370117418637049 0.0831071428571428 -0.00819047619047622 C 0.0953550597998766 -0.0125265741339082 0.107821870086751 -0.010397536991717 0.120321428571429 -0.0115555555555556 C 0.133179018681433 -0.0127467438724762 0.151318627483861 -0.0158700272533852 0.1585 0.00539682539682542 C 0.16478291361998 0.0240029898688431 0.15828642886492 0.0483806254341085 0.161392857142857 0.0698412698412698 C 0.165179286017685 0.0959996731216037 0.17453898927982 0.119735912694626 0.187142857142857 0.132634920634921 C 0.199788991845377 0.145577185161529 0.215607110490848 0.142889773028431 0.230107142857143 0.142857142857143 C 0.243821417584191 0.142826280916829 0.257716514999779 0.142685979556724 0.271142857142857 0.137777777777778 C 0.286895094567923 0.132019309914514 0.302318190711873 0.122962218306185 0.317928571428571 0.11568253968254 C 0.333496771884548 0.108422531222479 0.348787823719556 0.0990570571890929 0.363714285714286 0.0885079365079364 C 0.374930683062651 0.080580865157908 0.385357142857143 0.0693333333333332 0.396178571428571 0.0596825396825396 L 0.404785714285714 0.0410158730158729 L 0.401892857142857 0.0342222222222221 E", getXPath(pXmlDocContent, "(//p:animMotion)[1]", "path"));
|
||||
CPPUNIT_ASSERT_MOTIONPATH("M 0.025 0.0571428571428571 L 0.0821428571428571 0.184126984126984 L -0.175 0.234920634920635 L -0.246428571428571 -0.0190476190476191 L -0.0821428571428573 -0.133333333333333 E", getXPath(pXmlDocContent, "(//p:animMotion)[2]", "path"));
|
||||
CPPUNIT_ASSERT_MOTIONPATH("M -0.0107142857142857 0.00634920634920635 C -0.110714285714286 0.501587301587301 -0.153571428571429 -0.00634920634920635 -0.246428571428572 0.184126984126984 C -0.339285714285715 0.374603174603175 -0.296428571428572 0.514285714285714 -0.267857142857143 0.603174603174603 C -0.239285714285715 0.692063492063492 0.0607142857142858 0.590476190476191 0.0607142857142858 0.590476190476191 E", getXPath(pXmlDocContent, "(//p:animMotion)[3]", "path"));
|
||||
CPPUNIT_ASSERT_MOTIONPATH("M 0.0535714285714286 -0.0444444444444444 L 0.132142857142857 -0.0444444444444444 L 0.132142857142857 -0.146031746031746 L 0.0964285714285715 -0.146031746031746 E", getXPath(pXmlDocContent, "(//p:animMotion)[4]", "path"));
|
||||
|
||||
Reference in New Issue
Block a user