/* -*- 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 ) return0.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)?
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.17Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-06)
¤
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.