Index: klocale.h =================================================================== --- klocale.h (revision 424104) +++ klocale.h (working copy) @@ -389,14 +389,41 @@ * e.g. given 123456.78F, return "123,456.78" (for some European country). * If precision isn't specified, 2 is used. * + * This function is a wrapper that is provided for convenience. + * * @param num The number to convert * @param precision Number of fractional digits used. * * @return The number as a localized string + * @see formatNumber(const QString, bool, int) */ QString formatNumber(double num, int precision = -1) const; /** + * @deprecated + * + * KDE 4.0: merge with formatNumber(const QString int) + * + * calls formatNumber(numStr, 2) + */ + QString formatNumber(const QString &numStr) const KDE_DEPRECATED; + + /** + * Given a string representing a number, converts that to a numeric + * string containing the localized numeric equivalent. + * + * e.g. given 123456.78F, return "123,456.78" (for some European country). + * + * @param numStr The number to convert + * @param round Round fractional digits. + * @param precision Number of fractional digits used. + * + * @return The number as a localized string + * @since 3.5 + */ + QString formatNumber(const QString &numStr, bool round, int precision) const; + + /** * Given an integer, converts that to a numeric string containing * the localized numeric equivalent. * @@ -1118,12 +1145,6 @@ /** * @deprecated - * use formatNumber(double) - */ - QString formatNumber(const QString &numStr) const KDE_DEPRECATED; - - /** - * @deprecated * Use languageList() * * @return String containing language codes separated by colons Index: tests/klocaletest.cpp =================================================================== --- tests/klocaletest.cpp (revision 424104) +++ tests/klocaletest.cpp (working copy) @@ -114,6 +114,17 @@ formatted = KGlobal::locale()->formatNumber( 70.245 ); check("formatNumber(70.245)",formatted,"70.25"); /*rounded*/ formatted = KGlobal::locale()->formatNumber(1234567.89123456789,8); check("formatNumber(1234567.89123456789,8)",formatted,"1,234,567.89123457"); + formatted = KGlobal::locale()->formatNumber("70"); check("formatNumber(\"70\")",formatted,"70.00"); + formatted = KGlobal::locale()->formatNumber("70", true, 2); check("formatNumber(\"70\", true, 2)",formatted,"70.00"); + formatted = KGlobal::locale()->formatNumber("70", true, 0); check("formatNumber(\"70\", true, 0)",formatted,"70"); + formatted = KGlobal::locale()->formatNumber("-70.2", true, 2); check("formatNumber(\"-70.2\", true, 2)",formatted,"-70.20"); + formatted = KGlobal::locale()->formatNumber("+70.24", true, 2); check("formatNumber(\"+70.24\", true, 2)",formatted,"70.24"); + formatted = KGlobal::locale()->formatNumber("70.245", true, 2); check("formatNumber(\"70.245\", true, 2)",formatted,"70.25"); /*rounded*/ + formatted = KGlobal::locale()->formatNumber("99.996", true, 2); check("formatNumber(\"99.996\", true, 2)",formatted,"100.00"); /*rounded*/ + formatted = KGlobal::locale()->formatNumber("12345678901234567.89123456789", false, 0); check("formatNumber(\"12345678901234567.89123456789\", false, 0)",formatted,"12,345,678,901,234,567.89123456789"); + + + double num; bool ok; num = KGlobal::locale()->readNumber( "12,1", &ok ); check("readNumber(12,1)",ok?"yes":"no","no"); Index: klocale.cpp =================================================================== --- klocale.cpp (revision 424104) +++ klocale.cpp (working copy) @@ -1171,20 +1171,8 @@ QString KLocale::formatNumber(double num, int precision) const { - bool neg = num < 0; if (precision == -1) precision = 2; - QString res = QString::number(neg?-num:num, 'f', precision); - int pos = res.find('.'); - if (pos == -1) pos = res.length(); - else res.replace(pos, 1, decimalSymbol()); - - while (0 < (pos -= 3)) - res.insert(pos, thousandsSeparator()); // thousand sep - - // How can we know where we should put the sign? - res.prepend(neg?negativeSign():positiveSign()); - - return res; + return formatNumber(QString::number(num, 'f', precision), false, 0); } QString KLocale::formatLong(long num) const @@ -1194,9 +1182,143 @@ QString KLocale::formatNumber(const QString &numStr) const { - return formatNumber(numStr.toDouble()); + return formatNumber(numStr, true, 2); } +// increase the digit at 'position' by one +static void _inc_by_one(QString &str, int position) +{ + for (int i = position; i >= 0; i--) + { + char last_char = str[i].latin1(); + switch(last_char) + { + case '0': + str[i] = '1'; + break; + case '1': + str[i] = '2'; + break; + case '2': + str[i] = '3'; + break; + case '3': + str[i] = '4'; + break; + case '4': + str[i] = '5'; + break; + case '5': + str[i] = '6'; + break; + case '6': + str[i] = '7'; + break; + case '7': + str[i] = '8'; + break; + case '8': + str[i] = '9'; + break; + case '9': + str[i] = '0'; + if (i == 0) str.prepend('1'); + continue; + case '.': + continue; + } + break; + } +} + +static void _round(QString &str, int precision) +{ + // Cut off if more digits in fractional part than 'precision' + int decimalSymbolPos = str.find('.'); + + if (precision == 0 && decimalSymbolPos == -1) return; + + // add dot if missing (and needed) + if (decimalSymbolPos == -1 && precision > 0) + { + str.append('.'); + decimalSymbolPos = str.length() - 1; + } + + // fill up with more than enough zeroes (in case number too short) + str.append(QString().fill('0', precision)); + + // Now decide whether to round up or down + char last_char = str[decimalSymbolPos + precision + 1].latin1(); + switch (last_char) + { + case '0': + case '1': + case '2': + case '3': + case '4': + // nothing to do, rounding down + break; + case '5': + case '6': + case '7': + case '8': + case '9': + _inc_by_one(str, decimalSymbolPos + precision); + break; + default: + break; + } + + decimalSymbolPos = str.find('.'); + str.truncate(decimalSymbolPos + precision + 1); + + // if precision == 0 delete also '.' + if (precision == 0) str = str.section('.', 0, 0); +} + + +QString KLocale::formatNumber(const QString &numStr, bool round, + int precision) const +{ + QString tmpString = numStr; + if ((round && precision < 0) || + ! QRegExp("^[+-]?\\d+(\\.\\d+)*(e[+-]?\\d+)?$").exactMatch(tmpString)) + return numStr; + + + // Skip the sign (for now) + if (tmpString[0] == '+') tmpString.remove(0, 1); + bool neg = (tmpString[0] == '-'); + if (neg) tmpString.remove(0, 1); + + // Split off exponential part (including 'e'-symbol) + QString mantString = tmpString.section('e', 0, 0, + QString::SectionCaseInsensitiveSeps); + QString expString = tmpString.section('e', 1, 1, + QString::SectionCaseInsensitiveSeps | + QString::SectionIncludeLeadingSep); + + if (round) _round(mantString, precision); + + // Replace dot with locale decimal separator + mantString.replace(QChar('.'), decimalSymbol()); + + // Insert the thousand separators + int pos = mantString.find(decimalSymbol()); + if (pos == -1) pos = mantString.length(); + pos --; + + for (int index=1; pos>0; pos--, index++) + if (index%3 == 0) + mantString.insert(pos, thousandsSeparator()); + + // How can we know where we should put the sign? + mantString.prepend(neg?negativeSign():positiveSign()); + + return mantString + expString; +} + QString KLocale::formatDate(const QDate &pDate, bool shortFormat) const { const QString rst = shortFormat?dateFormatShort():dateFormat();