/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * 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/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#define MAX_NO_OF_DIGITS DBL_DIG #define MAX_DOUBLE_BUFFER_LENGTH MAX_NO_OF_DIGITS + 9 // +1 for leading sign // +1 for digit before the decimal point // +1 for decimal point // +2 for exponent E and exp. leading sign // +3 for the exponent's value // +1 for closing 0
// Comment: Visual-Basic has a maximum of 12 positions after the // decimal point for floating-point-numbers. // all format-strings are compatible to Visual-Basic:
constexpr OUString GENERALNUMBER_FORMAT = u"0.############"_ustr;
constexpr OUString FIXED_FORMAT = u"0.00"_ustr;
constexpr OUString STANDARD_FORMAT = u"@0.00"_ustr;
constexpr OUString PERCENT_FORMAT = u"0.00%"_ustr;
constexpr OUString SCIENTIFIC_FORMAT = u"#.00E+00"_ustr; // Comment: the character @ means that thousand-separators shall // be generated. That's a StarBasic 'extension'.
staticdouble get_number_of_digits( double dNumber ) //double floor_log10_fabs( double dNumber )
{ if( dNumber==0.0 ) return 0.0; // used to be 1.0, now 0.0 because of #40025; else return floor( log10( fabs( dNumber ) ) );
}
// function to output an error-text (for debugging) // displaces all characters of the string, starting from nStartPos // for one position to larger indexes, i. e. place for a new // character (which is to be inserted) is created. // ATTENTION: the string MUST be long enough! inlinevoid SbxBasicFormater::ShiftString( OUStringBuffer& sStrg, sal_uInt16 nStartPos )
{
sStrg.remove(nStartPos,1);
}
// returns a flag if rounding a 9 void SbxBasicFormater::StrRoundDigit( OUStringBuffer& sStrg, short nPos, bool& bOverflow )
{ if( nPos<0 )
{ return;
}
bOverflow = false;
sal_Unicode c = sStrg[nPos]; if( nPos > 0 && (c == cDecPoint || c == cThousandSep) )
{
StrRoundDigit( sStrg, nPos - 1, bOverflow ); // CHANGE from 9.3.1997: end the method immediately after recursive call! return;
} // skip non-digits: // COMMENT: // in a valid format-string the number's output should be done // in one piece, i. e. special characters should ONLY be in // front OR behind the number and not right in the middle of // the format information for the number while( nPos >= 0 && ! rtl::isAsciiDigit(sStrg[nPos]))
{
nPos--;
} if( nPos==-1 )
{
ShiftString( sStrg, 0 );
sStrg[0] = '1';
bOverflow = true;
} else
{
sal_Unicode c2 = sStrg[nPos]; if( rtl::isAsciiDigit(c2) )
{ if( c2 == '9' )
{
sStrg[nPos] = '0';
StrRoundDigit( sStrg, nPos - 1, bOverflow );
} else
{
sStrg[nPos] = c2 + 1;
}
} else
{
ShiftString( sStrg,nPos+1 );
sStrg[nPos + 1] = '1';
bOverflow = true;
}
}
}
void SbxBasicFormater::StrRoundDigit( OUStringBuffer& sStrg, short nPos )
{ bool bOverflow;
short SbxBasicFormater::GetDigitAtPosScan( short nPos, bool& bFoundFirstDigit )
{ // trying to read a higher digit, // e. g. position 4 in 1.234, // or to read a digit outside of the // number's dissolution (double) if( nPos>nNumExp || abs(nNumExp-nPos)>MAX_NO_OF_DIGITS )
{ return NO_DIGIT_;
} // determine the index of the position in the number-string: // skip the leading sign
sal_uInt16 no = 1; // skip the decimal point if necessary if( nPos<nNumExp )
no++;
no += nNumExp-nPos; // query of the number's first valid digit --> set flag if( nPos==nNumExp )
bFoundFirstDigit = true; returnstatic_cast<short>(sSciNumStrg[ no ] - '0');
}
short SbxBasicFormater::GetDigitAtPosExpScan( short nPos, bool& bFoundFirstDigit )
{ if( nPos>nExpExp ) return -1;
// a value for the exponent can be given because the number maybe shall // not be displayed in a normed way (e. g. 1.2345e-03) but maybe 123.345e-3 ! short SbxBasicFormater::GetDigitAtPosExpScan( double dNewExponent, short nPos, bool& bFoundFirstDigit )
{
InitExp( dNewExponent );
// Copies the respective part of the format-string, if existing, and returns it. // So a new string is created, which has to be freed by the caller later.
OUString SbxBasicFormater::GetPosFormatString( std::u16string_view sFormatStrg, bool & bFound )
{
bFound = false; // default...
size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
if( nPos != std::u16string_view::npos )
{
bFound = true; // the format-string for positive numbers is // everything before the first ';' return OUString(sFormatStrg.substr( 0,nPos ));
}
if( nPos != std::u16string_view::npos )
{ // the format-string for the Null is // everything after the third ';'
std::u16string_view sTempStrg = sFormatStrg.substr( nPos+1 );
nPos = sTempStrg.find( FORMAT_SEPARATOR ); if( nPos != std::u16string_view::npos )
{
sTempStrg = sTempStrg.substr( nPos+1 );
nPos = sTempStrg.find( FORMAT_SEPARATOR ); if( nPos != std::u16string_view::npos )
{
bFound = true; return OUString(sTempStrg.substr( nPos+1 ));
}
}
}
return OUString();
}
// returns value <> 0 in case of an error void SbxBasicFormater::AnalyseFormatString( std::u16string_view sFormatStrg, short& nNoOfDigitsLeft, short& nNoOfDigitsRight, short& nNoOfOptionalDigitsLeft, short& nNoOfExponentDigits, short& nNoOfOptionalExponentDigits, bool& bPercent, bool& bCurrency, bool& bScientific, bool& bGenerateThousandSeparator, short& nMultipleThousandSeparators )
{
sal_Int32 nLen; short nState = 0;
nLen = sFormatStrg.size();
nNoOfDigitsLeft = 0;
nNoOfDigitsRight = 0;
nNoOfOptionalDigitsLeft = 0;
nNoOfExponentDigits = 0;
nNoOfOptionalExponentDigits = 0;
bPercent = false;
bCurrency = false;
bScientific = false; // from 11.7.97: as soon as a comma (point?) is found in the format string, // all three decimal powers are marked (i. e. thousand, million, ...)
bGenerateThousandSeparator = sFormatStrg.find( ',' ) != std::u16string_view::npos;
nMultipleThousandSeparators = 0;
for( sal_Int32 i = 0; i < nLen; i++ )
{
sal_Unicode c = sFormatStrg[ i ]; switch( c )
{ case'#': case'0': if( nState==0 )
{
nNoOfDigitsLeft++; // TODO here maybe better error inspection of the mantissa for valid syntax (see grammar)h // ATTENTION: 'undefined' behaviour if # and 0 are combined! // REMARK: #-placeholders are actually useless for // scientific display before the decimal point! if( c=='#' )
{
nNoOfOptionalDigitsLeft++;
}
} elseif( nState==1 )
{
nNoOfDigitsRight++;
} elseif( nState==-1 ) // search 0 in the exponent
{ if( c=='#' ) // # switches on the condition
{
nNoOfOptionalExponentDigits++;
nState = -2;
}
nNoOfExponentDigits++;
} elseif( nState==-2 ) // search # in the exponent
{ if( c=='0' )
{ // ERROR: 0 after # in the exponent is NOT allowed!! return;
}
nNoOfOptionalExponentDigits++;
nNoOfExponentDigits++;
} break; case'.':
nState++; if( nState>1 )
{ return; // ERROR: too many decimal points
} break; case'%':
bPercent = true; break; case'(':
bCurrency = true; break; case',':
{
sal_Unicode ch = sFormatStrg[ i+1 ];
if( ch!=0 && (ch==',' || ch=='.') )
{
nMultipleThousandSeparators++;
}
} break; case'e': case'E': // #i13821 not when no digits before if( nNoOfDigitsLeft > 0 || nNoOfDigitsRight > 0 )
{
nState = -1; // abort counting digits
bScientific = true;
} break; // OWN command-character which turns on // the creation of thousand-separators case'\\': // Ignore next char
i++; break; case CREATE_1000SEP_CHAR:
bGenerateThousandSeparator = true; break;
}
}
}
// the flag bCreateSign says that at the mantissa a leading sign // shall be created void SbxBasicFormater::ScanFormatString( double dNumber,
std::u16string_view sFormatStrg, OUString& sReturnStrgFinal, bool bCreateSign )
{ short/*nErr,*/nNoOfDigitsLeft,nNoOfDigitsRight,nNoOfOptionalDigitsLeft,
nNoOfExponentDigits,nNoOfOptionalExponentDigits,
nMultipleThousandSeparators; bool bPercent,bCurrency,bScientific,bGenerateThousandSeparator;
OUStringBuffer sReturnStrg(32);
// analyse the format-string, i. e. determine the following values: /* - number of digits before decimal point - number of digits after decimal point - optional digits before decimal point - number of digits in the exponent - optional digits in the exponent - percent-character found? - () for negative leading sign? - exponential-notation? - shall thousand-separators be generated? - is a percent-character being found? --> dNumber *= 100.0; - are there thousand-separators in a row? ,, or ,. --> dNumber /= 1000.0; - other errors? multiple decimal points, E's, etc. --> errors are simply ignored at the moment
*/
AnalyseFormatString( sFormatStrg, nNoOfDigitsLeft, nNoOfDigitsRight,
nNoOfOptionalDigitsLeft, nNoOfExponentDigits,
nNoOfOptionalExponentDigits,
bPercent, bCurrency, bScientific,
bGenerateThousandSeparator, nMultipleThousandSeparators ); // special handling for special characters if( bPercent )
{
dNumber *= 100.0;
} // TODO: this condition (,, or ,.) is NOT Visual-Basic compatible! // Question: shall this stay here (requirements)? if( nMultipleThousandSeparators )
{
dNumber /= 1000.0;
} double dExponent; short i,nLen; short nState,nDigitPos,nExponentPos,nMaxDigit,nMaxExponentDigit; bool bFirstDigit,bFirstExponentDigit,bFoundFirstDigit,
bIsNegative,bZeroSpaceOn, bSignHappend,bDigitPosNegative;
InitScan( dNumber ); // scanning the format-string:
sal_Unicode cForce = 0; for( i = 0; i < nLen; i++ )
{
sal_Unicode c; if( cForce )
{
c = cForce;
cForce = 0;
} else
{
c = sFormatStrg[ i ];
} switch( c )
{ case'0': case'#': if( nState==0 )
{ // handling of the mantissa if( bFirstDigit )
{ // remark: at bCurrency the negative // leading sign shall be shown with () if( bIsNegative && !bCreateSign && !bSignHappend )
{
bSignHappend = true;
sReturnStrg.append('-');
} // output redundant positions, i. e. those which // are undocumented by the format-string if( nMaxDigit > nDigitPos )
{ for( short j = nMaxDigit; j > nDigitPos; j-- )
{ short nTempDigit = GetDigitAtPosScan( j, bFoundFirstDigit );
AppendDigit( sReturnStrg, nTempDigit ); if( nTempDigit != NO_DIGIT_ )
{
bFirstDigit = false;
} if( bGenerateThousandSeparator && c=='0' && j > 0 && (j % 3 == 0) )
{
sReturnStrg.append(cThousandSep );
}
}
}
}
if( nMaxDigit<nDigitPos && ( c=='0' || bZeroSpaceOn ) )
{
AppendDigit( sReturnStrg, 0 );
bFirstDigit = false;
bZeroSpaceOn = true; // Remark: in Visual-Basic the first 0 turns on the 0 for // all the following # (up to the decimal point), // this behaviour is simulated here with the flag. if (bGenerateThousandSeparator && c == '0' && nDigitPos > 0 && (nDigitPos % 3 == 0))
{
sReturnStrg.append(cThousandSep);
}
} else
{ short nTempDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit ) ;
AppendDigit( sReturnStrg, nTempDigit );
if( nTempDigit != NO_DIGIT_ )
{
bFirstDigit = false;
} if( bGenerateThousandSeparator && ( c=='0' || nMaxDigit>=nDigitPos ) && nDigitPos>0 && (nDigitPos % 3 == 0) )
{
sReturnStrg.append(cThousandSep);
}
}
nDigitPos--;
} else
{ // handling the exponent if( bFirstExponentDigit )
{ // leading sign has been given out at e/E already
bFirstExponentDigit = false; if( nMaxExponentDigit > nExponentPos ) // output redundant positions, i. e. those which // are undocumented by the format-string
{ for( short j = nMaxExponentDigit; j > nExponentPos; j-- )
{
AppendDigit( sReturnStrg, GetDigitAtPosExpScan( dExponent, j, bFoundFirstDigit ) );
}
}
}
if( nMaxExponentDigit < nExponentPos && c=='0' )
{
AppendDigit( sReturnStrg, 0 );
} else
{
AppendDigit( sReturnStrg, GetDigitAtPosExpScan( dExponent, nExponentPos, bFoundFirstDigit ) );
}
nExponentPos--;
} break; case'.': if( bDigitPosNegative ) // #i13821: If no digits before .
{
bDigitPosNegative = false;
nDigitPos = 0;
cForce = '#';
i-=2; break;
}
sReturnStrg.append(cDecPoint); break; case'%': // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
ParseBack( sReturnStrg, sFormatStrg, i-1 );
sReturnStrg.append('%'); break; case'e': case'E': // does mantissa have to be rounded, before the exponent is displayed?
{ // is there a mantissa at all? if( bFirstDigit )
{ // apparently not, i. e. invalid format string, e. g. E000.00 // so ignore these e and E characters // maybe output an error (like in Visual Basic)?
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.