diff options
-rw-r--r-- | svl/inc/svl/zforlist.hxx | 13 | ||||
-rw-r--r-- | svl/inc/svl/zformat.hxx | 7 | ||||
-rw-r--r-- | svl/source/numbers/zforlist.cxx | 20 | ||||
-rw-r--r-- | svl/source/numbers/zformat.cxx | 180 | ||||
-rw-r--r-- | svl/source/numbers/zforscan.cxx | 2 | ||||
-rw-r--r-- | svl/source/numbers/zforscan.hxx | 6 |
6 files changed, 182 insertions, 46 deletions
diff --git a/svl/inc/svl/zforlist.hxx b/svl/inc/svl/zforlist.hxx index 00f2dbe92b4c..598295797ea6 100644 --- a/svl/inc/svl/zforlist.hxx +++ b/svl/inc/svl/zforlist.hxx @@ -333,6 +333,17 @@ class SvNumberFormatterRegistry_Impl; class SVL_DLLPUBLIC SvNumberFormatter { public: + /** + * We can't technically have an "infinite" value, so we use an arbitrary + * upper precision threshold to represent the "unlimited" precision. + */ + static const sal_uInt16 UNLIMITED_PRECISION; + + /** + * Precision suitable for numbers displayed in input bar, for instance + * Calc's formula input bar. + */ + static const sal_uInt16 INPUTSTRING_PRECISION; /// Preferred ctor with service manager and language/country enum SvNumberFormatter( @@ -583,7 +594,7 @@ public: /// Return the reference date Date* GetNullDate(); /// Return the standard decimal precision - short GetStandardPrec(); + sal_uInt16 GetStandardPrec(); /// Return whether zero suppression is switched on BOOL GetNoZero() { return bNoZero; } /** Get the type of a format (or NUMBERFORMAT_UNDEFINED if no entry), diff --git a/svl/inc/svl/zformat.hxx b/svl/inc/svl/zformat.hxx index 7cdcf4db518d..7c361382796a 100644 --- a/svl/inc/svl/zformat.hxx +++ b/svl/inc/svl/zformat.hxx @@ -236,6 +236,12 @@ public: // in fact that could be any string used in number formats. static void LoadString( SvStream& rStream, String& rStr ); + /** + * Get output string from a numeric value that fits the number of + * characters specified. + */ + bool GetOutputString( double fNumber, sal_uInt16 nCharCount, String& rOutString ) const; + BOOL GetOutputString( double fNumber, String& OutString, Color** ppColor ); BOOL GetOutputString( String& sString, String& OutString, Color** ppColor ); @@ -487,6 +493,7 @@ private: // standard number output SVL_DLLPRIVATE void ImpGetOutputStandard( double& fNumber, String& OutString ); + SVL_DLLPRIVATE void ImpGetOutputStdToPrecision( double& rNumber, String& rOutString, sal_uInt16 nPrecision ) const; // numbers in input line SVL_DLLPRIVATE void ImpGetOutputInputLine( double fNumber, String& OutString ); diff --git a/svl/source/numbers/zforlist.cxx b/svl/source/numbers/zforlist.cxx index 05dab66754e1..cb66a75558c2 100644 --- a/svl/source/numbers/zforlist.cxx +++ b/svl/source/numbers/zforlist.cxx @@ -61,6 +61,7 @@ #include <rtl/instance.hxx> #include <math.h> +#include <limits> using namespace ::com::sun::star; using namespace ::com::sun::star::uno; @@ -178,6 +179,9 @@ SV_IMPL_PTRARR( NfWSStringsDtor, String* ); /***********************Funktionen SvNumberFormatter**************************/ +const sal_uInt16 SvNumberFormatter::UNLIMITED_PRECISION = ::std::numeric_limits<sal_uInt16>::max(); +const sal_uInt16 SvNumberFormatter::INPUTSTRING_PRECISION = ::std::numeric_limits<sal_uInt16>::max()-1; + SvNumberFormatter::SvNumberFormatter( const Reference< XMultiServiceFactory >& xSMgr, LanguageType eLang ) @@ -338,7 +342,7 @@ void SvNumberFormatter::ChangeStandardPrec(short nPrec) pFormatScanner->ChangeStandardPrec(nPrec); } -short SvNumberFormatter::GetStandardPrec() +sal_uInt16 SvNumberFormatter::GetStandardPrec() { return pFormatScanner->GetStandardPrec(); } @@ -1473,7 +1477,6 @@ void SvNumberFormatter::GetInputLineString(const double& fOutNumber, String& sOutString) { SvNumberformat* pFormat; - short nOldPrec; Color* pColor; pFormat = (SvNumberformat*) aFTable.Get(nFIndex); if (!pFormat) @@ -1483,7 +1486,8 @@ void SvNumberFormatter::GetInputLineString(const double& fOutNumber, short eType = pFormat->GetType() & ~NUMBERFORMAT_DEFINED; if (eType == 0) eType = NUMBERFORMAT_DEFINED; - nOldPrec = -1; + sal_uInt16 nOldPrec = pFormatScanner->GetStandardPrec(); + bool bPrecChanged = false; if (eType == NUMBERFORMAT_NUMBER || eType == NUMBERFORMAT_PERCENT || eType == NUMBERFORMAT_CURRENCY || eType == NUMBERFORMAT_SCIENTIFIC @@ -1491,8 +1495,8 @@ void SvNumberFormatter::GetInputLineString(const double& fOutNumber, { if (eType != NUMBERFORMAT_PERCENT) // spaeter Sonderbehandlung % eType = NUMBERFORMAT_NUMBER; - nOldPrec = pFormatScanner->GetStandardPrec(); - ChangeStandardPrec(300); // Merkwert + ChangeStandardPrec(INPUTSTRING_PRECISION); + bPrecChanged = true; } sal_uInt32 nKey = nFIndex; switch ( eType ) @@ -1512,12 +1516,12 @@ void SvNumberFormatter::GetInputLineString(const double& fOutNumber, { if ( eType == NUMBERFORMAT_TIME && pFormat->GetFormatPrecision() ) { - nOldPrec = pFormatScanner->GetStandardPrec(); - ChangeStandardPrec(300); // Merkwert + ChangeStandardPrec(INPUTSTRING_PRECISION); + bPrecChanged = true; } pFormat->GetOutputString(fOutNumber, sOutString, &pColor); } - if (nOldPrec != -1) + if (bPrecChanged) ChangeStandardPrec(nOldPrec); } diff --git a/svl/source/numbers/zformat.cxx b/svl/source/numbers/zformat.cxx index 32c65b06d2f1..707b2362f0ed 100644 --- a/svl/source/numbers/zformat.cxx +++ b/svl/source/numbers/zformat.cxx @@ -54,6 +54,9 @@ #include "numhead.hxx" #include <unotools/digitgroupingiterator.hxx> #include "nfsymbol.hxx" + +#include <cmath> + using namespace svt; namespace { @@ -63,6 +66,10 @@ struct Gregorian return ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("gregorian")); } }; + +const sal_uInt16 UPPER_PRECISION = 300; // entirely arbitrary... +const double EXP_LOWER_BOUND = 1.0E-4; // prefer scientific notation below this value. + } const double _D_MAX_U_LONG_ = (double) 0xffffffff; // 4294967295.0 @@ -1774,47 +1781,62 @@ void SvNumberformat::Build50Formatstring( String& rStr ) const void SvNumberformat::ImpGetOutputStandard(double& fNumber, String& OutString) { - USHORT nStandardPrec = rScan.GetStandardPrec(); + sal_uInt16 nStandardPrec = rScan.GetStandardPrec(); + if ( fabs(fNumber) > 1.0E15 ) // #58531# war E16 + { + nStandardPrec = ::std::min(nStandardPrec, static_cast<sal_uInt16>(14)); // limits to 14 decimals OutString = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_E, nStandardPrec /*2*/, GetFormatter().GetNumDecimalSep().GetChar(0)); + } else - { + ImpGetOutputStdToPrecision(fNumber, OutString, nStandardPrec); +} + +void SvNumberformat::ImpGetOutputStdToPrecision(double& rNumber, String& rOutString, sal_uInt16 nPrecision) const +{ + // Make sure the precision doesn't go over the maximum allowable precision. + nPrecision = ::std::min(UPPER_PRECISION, nPrecision); + #if 0 { - // debugger test case for ANSI standard correctness - ::rtl::OUString aTest; - // expect 0.00123 OK - aTest = ::rtl::math::doubleToUString( 0.001234567, - rtl_math_StringFormat_G, 3, '.', sal_True ); - // expect 123 OK - aTest = ::rtl::math::doubleToUString( 123.4567, - rtl_math_StringFormat_G, 3, '.', sal_True ); - // expect 123.5 OK - aTest = ::rtl::math::doubleToUString( 123.4567, - rtl_math_StringFormat_G, 4, '.', sal_True ); - // expect 1e+03 (as 999.6 rounded to 3 significant digits results in - // 1000 with an exponent equal to significant digits) - // Currently (24-Jan-2003) we do fail in this case and output 1000 - // instead, negligible. - aTest = ::rtl::math::doubleToUString( 999.6, - rtl_math_StringFormat_G, 3, '.', sal_True ); - // expect what? result is 1.2e+004 - aTest = ::rtl::math::doubleToUString( 12345.6789, - rtl_math_StringFormat_G, -3, '.', sal_True ); + // debugger test case for ANSI standard correctness + ::rtl::OUString aTest; + // expect 0.00123 OK + aTest = ::rtl::math::doubleToUString( 0.001234567, + rtl_math_StringFormat_G, 3, '.', sal_True ); + // expect 123 OK + aTest = ::rtl::math::doubleToUString( 123.4567, + rtl_math_StringFormat_G, 3, '.', sal_True ); + // expect 123.5 OK + aTest = ::rtl::math::doubleToUString( 123.4567, + rtl_math_StringFormat_G, 4, '.', sal_True ); + // expect 1e+03 (as 999.6 rounded to 3 significant digits results in + // 1000 with an exponent equal to significant digits) + // Currently (24-Jan-2003) we do fail in this case and output 1000 + // instead, negligible. + aTest = ::rtl::math::doubleToUString( 999.6, + rtl_math_StringFormat_G, 3, '.', sal_True ); + // expect what? result is 1.2e+004 + aTest = ::rtl::math::doubleToUString( 12345.6789, + rtl_math_StringFormat_G, -3, '.', sal_True ); } #endif - OutString = ::rtl::math::doubleToUString( fNumber, - rtl_math_StringFormat_F, nStandardPrec /*2*/, - GetFormatter().GetNumDecimalSep().GetChar(0), sal_True ); - if (OutString.GetChar(0) == '-' && - OutString.GetTokenCount('0') == OutString.Len()) - OutString.EraseLeadingChars('-'); // nicht -0 - } - ImpTransliterate( OutString, NumFor[0].GetNatNum() ); - return; + // We decided to strip trailing zeros unconditionally, since binary + // double-precision rounding error makes it impossible to determine e.g. + // whether 844.10000000000002273737 is what the user has typed, or the + // user has typed 844.1 but IEEE 754 represents it that way internally. + + rOutString = ::rtl::math::doubleToUString( rNumber, + rtl_math_StringFormat_F, nPrecision /*2*/, + GetFormatter().GetNumDecimalSep().GetChar(0), true ); + if (rOutString.GetChar(0) == '-' && + rOutString.GetTokenCount('0') == rOutString.Len()) + rOutString.EraseLeadingChars('-'); // nicht -0 + + ImpTransliterate( rOutString, NumFor[0].GetNatNum() ); } void SvNumberformat::ImpGetOutputInputLine(double fNumber, String& OutString) @@ -1955,6 +1977,71 @@ ULONG SvNumberformat::ImpGGTRound(ULONG x, ULONG y) } } +namespace { + +void lcl_GetOutputStringScientific( + double fNumber, sal_uInt16 nCharCount, const SvNumberFormatter& rFormatter, String& rOutString) +{ + bool bSign = ::rtl::math::isSignBitSet(fNumber); + + // 1.000E+015 (one digit and the decimal point, and the five chars for the exponential part, totalling 7). + sal_uInt16 nPrec = nCharCount > 7 ? nCharCount - 7 : 0; + if (nPrec && bSign) + // Make room for the negative sign. + --nPrec; + + nPrec = ::std::min(nPrec, static_cast<sal_uInt16>(14)); // limit to 14 decimals. + + rOutString = ::rtl::math::doubleToUString( + fNumber, rtl_math_StringFormat_E, nPrec, rFormatter.GetNumDecimalSep().GetChar(0)); +} + +} + +bool SvNumberformat::GetOutputString(double fNumber, sal_uInt16 nCharCount, String& rOutString) const +{ + using namespace std; + + if (eType != NUMBERFORMAT_NUMBER) + return false; + + double fTestNum = fNumber; + bool bSign = ::rtl::math::isSignBitSet(fTestNum); + if (bSign) + fTestNum = -fTestNum; + + if (fTestNum < EXP_LOWER_BOUND) + { + lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString); + return true; + } + + double fExp = log10(fTestNum); + // Values < 1.0 always have one digit before the decimal point. + sal_uInt16 nDigitPre = fExp >= 0.0 ? static_cast<sal_uInt16>(ceil(fExp)) : 1; + + if (nDigitPre > 15) + { + lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString); + return true; + } + + sal_uInt16 nPrec = nCharCount >= nDigitPre ? nCharCount - nDigitPre : 0; + if (nPrec && bSign) + // Subtract the negative sign. + --nPrec; + if (nPrec) + // Subtract the decimal point. + --nPrec; + + ImpGetOutputStdToPrecision(fNumber, rOutString, nPrec); + if (rOutString.Len() > nCharCount) + // String still wider than desired. Switch to scientific notation. + lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString); + + return true; +} + BOOL SvNumberformat::GetOutputString(double fNumber, String& OutString, Color** ppColor) @@ -1978,16 +2065,43 @@ BOOL SvNumberformat::GetOutputString(double fNumber, BOOL bHadStandard = FALSE; if (bStandard) // einzelne Standardformate { - if (rScan.GetStandardPrec() == 300) // alle Zahlformate InputLine + if (rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION) // alle Zahlformate InputLine { ImpGetOutputInputLine(fNumber, OutString); - return FALSE; + return false; } switch (eType) { case NUMBERFORMAT_NUMBER: // Standardzahlformat + { + if (rScan.GetStandardPrec() == SvNumberFormatter::UNLIMITED_PRECISION) + { + bool bSign = ::rtl::math::isSignBitSet(fNumber); + if (bSign) + fNumber = -fNumber; + ImpGetOutputStdToPrecision(fNumber, OutString, 10); // Use 10 decimals for general 'unlimited' format. + if (fNumber < EXP_LOWER_BOUND) + { + xub_StrLen nLen = OutString.Len(); + if (!nLen) + return false; + + if (nLen > 11) + { + sal_uInt16 nStandardPrec = rScan.GetStandardPrec(); + nStandardPrec = ::std::min(nStandardPrec, static_cast<sal_uInt16>(14)); // limits to 14 decimals + OutString = ::rtl::math::doubleToUString( fNumber, + rtl_math_StringFormat_E, nStandardPrec /*2*/, + GetFormatter().GetNumDecimalSep().GetChar(0), true); + } + } + if (bSign) + OutString.Insert('-', 0); + return false; + } ImpGetOutputStandard(fNumber, OutString); bHadStandard = TRUE; + } break; case NUMBERFORMAT_DATE: bRes |= ImpGetDateOutput(fNumber, 0, OutString); diff --git a/svl/source/numbers/zforscan.cxx b/svl/source/numbers/zforscan.cxx index 5ed78bd69108..fb59e4289ad1 100644 --- a/svl/source/numbers/zforscan.cxx +++ b/svl/source/numbers/zforscan.cxx @@ -465,7 +465,7 @@ void ImpSvNumberformatScan::ChangeNullDate(USHORT nDay, USHORT nMonth, USHORT nY pNullDate = new Date(nDay, nMonth, nYear); } -void ImpSvNumberformatScan::ChangeStandardPrec(short nPrec) +void ImpSvNumberformatScan::ChangeStandardPrec(sal_uInt16 nPrec) { nStandardPrec = nPrec; } diff --git a/svl/source/numbers/zforscan.hxx b/svl/source/numbers/zforscan.hxx index fb9e40488e24..d623a3f50026 100644 --- a/svl/source/numbers/zforscan.hxx +++ b/svl/source/numbers/zforscan.hxx @@ -54,7 +54,7 @@ public: void ChangeNullDate(USHORT nDay, USHORT nMonth, USHORT nYear); // tauscht Referenzdatum aus - void ChangeStandardPrec(short nPrec); // tauscht Standardprecision aus + void ChangeStandardPrec(sal_uInt16 nPrec); // tauscht Standardprecision aus xub_StrLen ScanFormat( String& rString, String& rComment ); // Aufruf der Scan-Analyse @@ -93,7 +93,7 @@ public: InitKeywords(); return sNameStandardFormat; } - short GetStandardPrec() const { return nStandardPrec; } + sal_uInt16 GetStandardPrec() const { return nStandardPrec; } const Color& GetRedColor() const { return StandardColor[4]; } Color* GetColor(String& sStr); // Setzt Hauptfarben oder // definierte Farben @@ -158,7 +158,7 @@ private: // ---- privater Teil // Array der Standardfarben Date* pNullDate; // 30Dec1899 String sNameStandardFormat; // "Standard" - short nStandardPrec; // default Precision fuer Standardformat (2) + sal_uInt16 nStandardPrec; // default Precision for Standardformat SvNumberFormatter* pFormatter; // Pointer auf die Formatliste String sStrArray[NF_MAX_FORMAT_SYMBOLS]; // Array der Symbole |