diff --git a/Foundation/include/Poco/NumberFormatter.h b/Foundation/include/Poco/NumberFormatter.h index 819f4515c..fb064b1e3 100644 --- a/Foundation/include/Poco/NumberFormatter.h +++ b/Foundation/include/Poco/NumberFormatter.h @@ -208,6 +208,15 @@ public: /// Formats a float value in decimal floating-point notation, /// according to std::printf's %g format with a precision of 8 fractional digits. + static std::string format(float value, int precision); + /// Formats a double value in decimal floating-point notation, + /// according to std::printf's %f format with the given precision. + + static std::string format(float value, int width, int precision); + /// Formats a double value in decimal floating-point notation, + /// right justified in a field of the specified width, + /// with the number of fractional digits given in precision. + static std::string format(double value); /// Formats a double value in decimal floating-point notation, /// according to std::printf's %g format with a precision of 16 fractional digits. @@ -368,6 +377,15 @@ public: /// Formats a float value in decimal floating-point notation, /// according to std::printf's %g format with a precision of 8 fractional digits. + static void append(std::string& str, float value, int precision); + /// Formats a double value in decimal floating-point notation, + /// according to std::printf's %f format with the given precision. + + static void append(std::string& str, float value, int width, int precision); + /// Formats a double value in decimal floating-point notation, + /// right justified in a field of the specified width, + /// with the number of fractional digits given in precision. + static void append(std::string& str, double value); /// Formats a double value in decimal floating-point notation, /// according to std::printf's %g format with a precision of 16 fractional digits. @@ -641,33 +659,49 @@ inline std::string NumberFormatter::formatHex(UInt64 value, int width, bool pref inline std::string NumberFormatter::format(float value) +{ + char buffer[POCO_MAX_FLT_STRING_LEN]; + floatToStr(buffer, POCO_MAX_FLT_STRING_LEN, value); + return std::string(buffer); +} + + +inline std::string NumberFormatter::format(float value, int precision) +{ + char buffer[POCO_MAX_FLT_STRING_LEN]; + floatToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); + return std::string(buffer); +} + + +inline std::string NumberFormatter::format(float value, int width, int precision) { std::string result; - floatToStr(result, value); + floatToFixedStr(result, value, precision, width); return result; } inline std::string NumberFormatter::format(double value) { - std::string result; - doubleToStr(result, value); - return result; + char buffer[POCO_MAX_FLT_STRING_LEN]; + doubleToStr(buffer, POCO_MAX_FLT_STRING_LEN, value); + return std::string(buffer); } inline std::string NumberFormatter::format(double value, int precision) { - std::string result; - doubleToStr(result, value, precision); - return result; + char buffer[POCO_MAX_FLT_STRING_LEN]; + doubleToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); + return std::string(buffer); } inline std::string NumberFormatter::format(double value, int width, int precision) { std::string result; - doubleToStr(result, value, precision, width); + doubleToFixedStr(result, value, precision, width); return result; } diff --git a/Foundation/include/Poco/NumericString.h b/Foundation/include/Poco/NumericString.h index c60cb147c..bd3dd900c 100644 --- a/Foundation/include/Poco/NumericString.h +++ b/Foundation/include/Poco/NumericString.h @@ -35,6 +35,7 @@ #include #endif + // binary numbers are supported, thus 64 (bits) + 1 (string terminating zero) #define POCO_MAX_INT_STRING_LEN 65 // value from strtod.cc (double_conversion::kMaxSignificantDecimalDigits) @@ -441,16 +442,25 @@ bool uIntToStr (T number, unsigned short base, std::string& result, bool prefix // http://florian.loitsch.com/publications/dtoa-pldi2010.pdf // + Foundation_API void floatToStr(char* buffer, int bufferSize, float value, - int lowDec = -std::numeric_limits::digits10, - int highDec = std::numeric_limits::digits10); + int lowDec = -std::numeric_limits::digits10, + int highDec = std::numeric_limits::digits10); /// Converts a float value to string. Converted string must be shorter than bufferSize. /// Conversion is done by computing the shortest string of digits that correctly represents /// the input number. Depending on lowDec and highDec values, the function returns /// decimal or exponential representation. +Foundation_API void floatToFixedStr(char* buffer, + int bufferSize, + float value, + int precision); + /// Converts a float value to string. Converted string must be shorter than bufferSize. + /// Computes a decimal representation with a fixed number of digits after the + /// decimal point. + Foundation_API std::string& floatToStr(std::string& str, float value, @@ -464,6 +474,17 @@ Foundation_API std::string& floatToStr(std::string& str, /// and width (total length of formatted string). +Foundation_API std::string& floatToFixedStr(std::string& str, + float value, + int precision, + int width = 0, + char thSep = 0, + char decSep = 0); + /// Converts a float value, assigns it to the supplied string and returns the reference. + /// This function calls floatToFixedStr(char*, int, float, int) and formats the result according to + /// precision (total number of digits after the decimal point) and width (total length of formatted string). + + Foundation_API void doubleToStr(char* buffer, int bufferSize, double value, @@ -475,6 +496,15 @@ Foundation_API void doubleToStr(char* buffer, /// decimal or exponential representation. +Foundation_API void doubleToFixedStr(char* buffer, + int bufferSize, + double value, + int precision); + /// Converts a double value to string. Converted string must be shorter than bufferSize. + /// Computes a decimal representation with a fixed number of digits after the + /// decimal point. + + Foundation_API std::string& doubleToStr(std::string& str, double value, int precision = -1, @@ -482,11 +512,22 @@ Foundation_API std::string& doubleToStr(std::string& str, char thSep = 0, char decSep = 0); /// Converts a double value, assigns it to the supplied string and returns the reference. - /// This function calls doubleToStr(char*, int, float, int, int) and formats the result according to + /// This function calls doubleToStr(char*, int, double, int, int) and formats the result according to /// precision (total number of digits after the decimal point, -1 means ignore precision argument) /// and width (total length of formatted string). +Foundation_API std::string& doubleToFixedStr(std::string& str, + double value, + int precision = -1, + int width = 0, + char thSep = 0, + char decSep = 0); + /// Converts a double value, assigns it to the supplied string and returns the reference. + /// This function calls doubleToFixedStr(char*, int, double, int) and formats the result according to + /// precision (total number of digits after the decimal point) and width (total length of formatted string). + + Foundation_API float strToFloat(const char* str); /// Converts the string of characters into single-precision floating point number. /// Function uses double_convesrion::DoubleToStringConverter to do the conversion. @@ -513,10 +554,6 @@ Foundation_API bool strToDouble(const std::string& str, double& result, char dec /// /// Returns true if succesful, false otherwise. -// -// end double-conversion functions declarations -// - } // namespace Poco diff --git a/Foundation/src/NumberFormatter.cpp b/Foundation/src/NumberFormatter.cpp index 45fdb3d0a..5df63e452 100644 --- a/Foundation/src/NumberFormatter.cpp +++ b/Foundation/src/NumberFormatter.cpp @@ -340,6 +340,21 @@ void NumberFormatter::append(std::string& str, float value) } +void NumberFormatter::append(std::string& str, float value, int precision) +{ + char buffer[NF_MAX_FLT_STRING_LEN]; + floatToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); + str.append(buffer); +} + + +void NumberFormatter::append(std::string& str, float value, int width, int precision) +{ + std::string result; + str.append(floatToFixedStr(result, value, precision, width)); +} + + void NumberFormatter::append(std::string& str, double value) { char buffer[NF_MAX_FLT_STRING_LEN]; @@ -350,15 +365,16 @@ void NumberFormatter::append(std::string& str, double value) void NumberFormatter::append(std::string& str, double value, int precision) { - std::string result; - str.append(doubleToStr(result, value, precision)); + char buffer[NF_MAX_FLT_STRING_LEN]; + doubleToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); + str.append(buffer); } void NumberFormatter::append(std::string& str, double value, int width, int precision) { std::string result; - str.append(doubleToStr(result, value, precision, width)); + str.append(doubleToFixedStr(result, value, precision, width)); } diff --git a/Foundation/src/NumericString.cpp b/Foundation/src/NumericString.cpp index 1dc666bc0..199af5771 100644 --- a/Foundation/src/NumericString.cpp +++ b/Foundation/src/NumericString.cpp @@ -140,6 +140,19 @@ void floatToStr(char* buffer, int bufferSize, float value, int lowDec, int highD } +void floatToFixedStr(char* buffer, int bufferSize, float value, int precision) +{ + using namespace double_conversion; + + StringBuilder builder(buffer, bufferSize); + int flags = DoubleToStringConverter::UNIQUE_ZERO | + DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; + DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, -std::numeric_limits::digits10, std::numeric_limits::digits10, 0, 0); + dc.ToFixed(value, precision, &builder); + builder.Finalize(); +} + + std::string& floatToStr(std::string& str, float value, int precision, int width, char thSep, char decSep) { if (!decSep) decSep = '.'; @@ -158,6 +171,24 @@ std::string& floatToStr(std::string& str, float value, int precision, int width, } +std::string& floatToFixedStr(std::string& str, float value, int precision, int width, char thSep, char decSep) +{ + if (!decSep) decSep = '.'; + if (precision == 0) value = std::floor(value); + + char buffer[POCO_MAX_FLT_STRING_LEN]; + floatToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); + str = buffer; + + if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) + replaceInPlace(str, '.', decSep); + + if (thSep) insertThousandSep(str, thSep, decSep); + if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); + return str; +} + + void doubleToStr(char* buffer, int bufferSize, double value, int lowDec, int highDec) { using namespace double_conversion; @@ -171,6 +202,19 @@ void doubleToStr(char* buffer, int bufferSize, double value, int lowDec, int hig } +void doubleToFixedStr(char* buffer, int bufferSize, double value, int precision) +{ + using namespace double_conversion; + + StringBuilder builder(buffer, bufferSize); + int flags = DoubleToStringConverter::UNIQUE_ZERO | + DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; + DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, -std::numeric_limits::digits10, std::numeric_limits::digits10, 0, 0); + dc.ToFixed(value, precision, &builder); + builder.Finalize(); +} + + std::string& doubleToStr(std::string& str, double value, int precision, int width, char thSep, char decSep) { if (!decSep) decSep = '.'; @@ -178,6 +222,26 @@ std::string& doubleToStr(std::string& str, double value, int precision, int widt char buffer[POCO_MAX_FLT_STRING_LEN]; doubleToStr(buffer, POCO_MAX_FLT_STRING_LEN, value); + + str = buffer; + + if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) + replaceInPlace(str, '.', decSep); + + if (thSep) insertThousandSep(str, thSep, decSep); + if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); + return str; +} + + +std::string& doubleToFixedStr(std::string& str, double value, int precision, int width, char thSep, char decSep) +{ + if (!decSep) decSep = '.'; + if (precision == 0) value = std::floor(value); + + char buffer[POCO_MAX_FLT_STRING_LEN]; + doubleToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); + str = buffer; if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) diff --git a/Foundation/testsuite/src/NumberFormatterTest.cpp b/Foundation/testsuite/src/NumberFormatterTest.cpp index e6935a711..a25a2bc5b 100644 --- a/Foundation/testsuite/src/NumberFormatterTest.cpp +++ b/Foundation/testsuite/src/NumberFormatterTest.cpp @@ -16,6 +16,7 @@ #include "Poco/NumberFormatter.h" #include + using Poco::NumberFormatter; using Poco::Int64; using Poco::UInt64; @@ -208,11 +209,13 @@ void NumberFormatterTest::testFormatFloat() assert(NumberFormatter::format(12.25, 4) == "12.2500"); assert(NumberFormatter::format(12.25, 8, 4) == " 12.2500"); + assert (NumberFormatter::format(12.45f, 2) == "12.45"); + assert(NumberFormatter::format(-12.25) == "-12.25"); assert(NumberFormatter::format(-12.25, 4) == "-12.2500"); assert(NumberFormatter::format(-12.25, 10, 4) == " -12.2500"); assert(NumberFormatter::format(-12.25, 10, 2) == " -12.25"); - + assert(NumberFormatter::format(-12.25, 10, 1) == " -12.3"); assert (NumberFormatter::format(50.0, 3) == "50.000"); assert (NumberFormatter::format(50.0f, 3) == "50.000"); @@ -220,8 +223,9 @@ void NumberFormatterTest::testFormatFloat() assert (NumberFormatter::format(50.123f, 3) == "50.123"); assert (NumberFormatter::format(50.123, 0) == "50"); assert (NumberFormatter::format(50.123f, 0) == "50"); - assert (NumberFormatter::format(50.546, 0) == "50"); - assert (NumberFormatter::format(50.546f, 0) == "50"); + assert (NumberFormatter::format(50.546, 0) == "51"); + assert (NumberFormatter::format(50.546f, 0) == "51"); + assert (NumberFormatter::format(50.546f, 2) == "50.55"); } @@ -258,7 +262,7 @@ void NumberFormatterTest::testAppend() assert (s == "123.4"); s.erase(); NumberFormatter::append(s, 123.4567, 2); - assert (s == "123.45"); + assert (s == "123.46"); s.erase(); NumberFormatter::append(s, 123.4567, 10, 5); assert (s == " 123.45670"); @@ -269,7 +273,7 @@ void NumberFormatterTest::testAppend() NumberFormatter::append(s, static_cast(1234567), 2); assert (s == "1234567.00"); s.erase(); - NumberFormatter::append(s, 1234567, 10, 1); + NumberFormatter::append(s, 1234567.0, 10, 1); assert (s == " 1234567.0"); } diff --git a/Foundation/testsuite/src/StringTest.cpp b/Foundation/testsuite/src/StringTest.cpp index a317c5413..0bc87b15a 100644 --- a/Foundation/testsuite/src/StringTest.cpp +++ b/Foundation/testsuite/src/StringTest.cpp @@ -927,7 +927,6 @@ void StringTest::testIntToString() assert (result == "11110000111100001111000011110000"); #if defined(POCO_HAVE_INT64) assert (uIntToStr(0xFFFFFFFFFFFFFFFF, 2, result)); - std::cout << 0xFFFFFFFFFFFFFFFF << std::endl; assert (result == "1111111111111111111111111111111111111111111111111111111111111111"); assert (uIntToStr(0xFF00000FF00000FF, 2, result)); assert (result == "1111111100000000000000000000111111110000000000000000000011111111");