[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-core-devel
Subject: Re: KLocale::formatNumber
From: Klaus Niederkrueger <kniederk () math ! uni-koeln ! de>
Date: 2005-06-14 23:17:25
Message-ID: Pine.GSO.4.44.0506150112170.7508-200000 () Thales
[Download RAW message or body]
Please review the code and tell me whether you think I can apply it to
kdecore or not? Other suggestions?
What does it do:
It basically adds a new "formatNumber"-method in klocale, which supports
arbitrarily large numbers (as QString) and one can switch off rounding.
What is it good for? In "kcalc" there was an implementation of such a
routine, but I thought let's make it available to everyone, and it is nice
to have a formatNumber that only rounds optionally.
Thanks
Klaus
["out.txt" (TEXT/PLAIN)]
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();
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic