fixed GH #1128: Poco::NumberFormatter::format(float, precision) rounding changed

This commit is contained in:
Guenter Obiltschnig 2016-02-28 13:32:54 +01:00
parent 539eba72d6
commit 8c607d3213
6 changed files with 178 additions and 24 deletions

View File

@ -208,6 +208,15 @@ public:
/// Formats a float value in decimal floating-point notation, /// Formats a float value in decimal floating-point notation,
/// according to std::printf's %g format with a precision of 8 fractional digits. /// 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); static std::string format(double value);
/// Formats a double value in decimal floating-point notation, /// Formats a double value in decimal floating-point notation,
/// according to std::printf's %g format with a precision of 16 fractional digits. /// 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, /// Formats a float value in decimal floating-point notation,
/// according to std::printf's %g format with a precision of 8 fractional digits. /// 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); static void append(std::string& str, double value);
/// Formats a double value in decimal floating-point notation, /// Formats a double value in decimal floating-point notation,
/// according to std::printf's %g format with a precision of 16 fractional digits. /// 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) 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; std::string result;
floatToStr(result, value); floatToFixedStr(result, value, precision, width);
return result; return result;
} }
inline std::string NumberFormatter::format(double value) inline std::string NumberFormatter::format(double value)
{ {
std::string result; char buffer[POCO_MAX_FLT_STRING_LEN];
doubleToStr(result, value); doubleToStr(buffer, POCO_MAX_FLT_STRING_LEN, value);
return result; return std::string(buffer);
} }
inline std::string NumberFormatter::format(double value, int precision) inline std::string NumberFormatter::format(double value, int precision)
{ {
std::string result; char buffer[POCO_MAX_FLT_STRING_LEN];
doubleToStr(result, value, precision); doubleToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision);
return result; return std::string(buffer);
} }
inline std::string NumberFormatter::format(double value, int width, int precision) inline std::string NumberFormatter::format(double value, int width, int precision)
{ {
std::string result; std::string result;
doubleToStr(result, value, precision, width); doubleToFixedStr(result, value, precision, width);
return result; return result;
} }

View File

@ -35,6 +35,7 @@
#include <locale> #include <locale>
#endif #endif
// binary numbers are supported, thus 64 (bits) + 1 (string terminating zero) // binary numbers are supported, thus 64 (bits) + 1 (string terminating zero)
#define POCO_MAX_INT_STRING_LEN 65 #define POCO_MAX_INT_STRING_LEN 65
// value from strtod.cc (double_conversion::kMaxSignificantDecimalDigits) // 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 // http://florian.loitsch.com/publications/dtoa-pldi2010.pdf
// //
Foundation_API void floatToStr(char* buffer, Foundation_API void floatToStr(char* buffer,
int bufferSize, int bufferSize,
float value, float value,
int lowDec = -std::numeric_limits<double>::digits10, int lowDec = -std::numeric_limits<float>::digits10,
int highDec = std::numeric_limits<double>::digits10); int highDec = std::numeric_limits<float>::digits10);
/// Converts a float value to string. Converted string must be shorter than bufferSize. /// 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 /// 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 /// the input number. Depending on lowDec and highDec values, the function returns
/// decimal or exponential representation. /// 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, Foundation_API std::string& floatToStr(std::string& str,
float value, float value,
@ -464,6 +474,17 @@ Foundation_API std::string& floatToStr(std::string& str,
/// and width (total length of formatted string). /// 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, Foundation_API void doubleToStr(char* buffer,
int bufferSize, int bufferSize,
double value, double value,
@ -475,6 +496,15 @@ Foundation_API void doubleToStr(char* buffer,
/// decimal or exponential representation. /// 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, Foundation_API std::string& doubleToStr(std::string& str,
double value, double value,
int precision = -1, int precision = -1,
@ -482,11 +512,22 @@ Foundation_API std::string& doubleToStr(std::string& str,
char thSep = 0, char thSep = 0,
char decSep = 0); char decSep = 0);
/// Converts a double value, assigns it to the supplied string and returns the reference. /// 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) /// precision (total number of digits after the decimal point, -1 means ignore precision argument)
/// and width (total length of formatted string). /// 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); Foundation_API float strToFloat(const char* str);
/// Converts the string of characters into single-precision floating point number. /// Converts the string of characters into single-precision floating point number.
/// Function uses double_convesrion::DoubleToStringConverter to do the conversion. /// 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. /// Returns true if succesful, false otherwise.
//
// end double-conversion functions declarations
//
} // namespace Poco } // namespace Poco

View File

@ -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) void NumberFormatter::append(std::string& str, double value)
{ {
char buffer[NF_MAX_FLT_STRING_LEN]; 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) void NumberFormatter::append(std::string& str, double value, int precision)
{ {
std::string result; char buffer[NF_MAX_FLT_STRING_LEN];
str.append(doubleToStr(result, value, precision)); doubleToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision);
str.append(buffer);
} }
void NumberFormatter::append(std::string& str, double value, int width, int precision) void NumberFormatter::append(std::string& str, double value, int width, int precision)
{ {
std::string result; std::string result;
str.append(doubleToStr(result, value, precision, width)); str.append(doubleToFixedStr(result, value, precision, width));
} }

View File

@ -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<float>::digits10, std::numeric_limits<float>::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) std::string& floatToStr(std::string& str, float value, int precision, int width, char thSep, char decSep)
{ {
if (!decSep) 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) void doubleToStr(char* buffer, int bufferSize, double value, int lowDec, int highDec)
{ {
using namespace double_conversion; 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<double>::digits10, std::numeric_limits<double>::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) std::string& doubleToStr(std::string& str, double value, int precision, int width, char thSep, char decSep)
{ {
if (!decSep) 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]; char buffer[POCO_MAX_FLT_STRING_LEN];
doubleToStr(buffer, POCO_MAX_FLT_STRING_LEN, value); 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; str = buffer;
if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) if (decSep && (decSep != '.') && (str.find('.') != std::string::npos))

View File

@ -16,6 +16,7 @@
#include "Poco/NumberFormatter.h" #include "Poco/NumberFormatter.h"
#include <sstream> #include <sstream>
using Poco::NumberFormatter; using Poco::NumberFormatter;
using Poco::Int64; using Poco::Int64;
using Poco::UInt64; using Poco::UInt64;
@ -208,11 +209,13 @@ void NumberFormatterTest::testFormatFloat()
assert(NumberFormatter::format(12.25, 4) == "12.2500"); assert(NumberFormatter::format(12.25, 4) == "12.2500");
assert(NumberFormatter::format(12.25, 8, 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) == "-12.25");
assert(NumberFormatter::format(-12.25, 4) == "-12.2500"); assert(NumberFormatter::format(-12.25, 4) == "-12.2500");
assert(NumberFormatter::format(-12.25, 10, 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, 2) == " -12.25");
assert(NumberFormatter::format(-12.25, 10, 1) == " -12.3");
assert (NumberFormatter::format(50.0, 3) == "50.000"); assert (NumberFormatter::format(50.0, 3) == "50.000");
assert (NumberFormatter::format(50.0f, 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.123f, 3) == "50.123");
assert (NumberFormatter::format(50.123, 0) == "50"); assert (NumberFormatter::format(50.123, 0) == "50");
assert (NumberFormatter::format(50.123f, 0) == "50"); assert (NumberFormatter::format(50.123f, 0) == "50");
assert (NumberFormatter::format(50.546, 0) == "50"); assert (NumberFormatter::format(50.546, 0) == "51");
assert (NumberFormatter::format(50.546f, 0) == "50"); 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"); assert (s == "123.4");
s.erase(); s.erase();
NumberFormatter::append(s, 123.4567, 2); NumberFormatter::append(s, 123.4567, 2);
assert (s == "123.45"); assert (s == "123.46");
s.erase(); s.erase();
NumberFormatter::append(s, 123.4567, 10, 5); NumberFormatter::append(s, 123.4567, 10, 5);
assert (s == " 123.45670"); assert (s == " 123.45670");
@ -269,7 +273,7 @@ void NumberFormatterTest::testAppend()
NumberFormatter::append(s, static_cast<double>(1234567), 2); NumberFormatter::append(s, static_cast<double>(1234567), 2);
assert (s == "1234567.00"); assert (s == "1234567.00");
s.erase(); s.erase();
NumberFormatter::append(s, 1234567, 10, 1); NumberFormatter::append(s, 1234567.0, 10, 1);
assert (s == " 1234567.0"); assert (s == " 1234567.0");
} }

View File

@ -927,7 +927,6 @@ void StringTest::testIntToString()
assert (result == "11110000111100001111000011110000"); assert (result == "11110000111100001111000011110000");
#if defined(POCO_HAVE_INT64) #if defined(POCO_HAVE_INT64)
assert (uIntToStr(0xFFFFFFFFFFFFFFFF, 2, result)); assert (uIntToStr(0xFFFFFFFFFFFFFFFF, 2, result));
std::cout << 0xFFFFFFFFFFFFFFFF << std::endl;
assert (result == "1111111111111111111111111111111111111111111111111111111111111111"); assert (result == "1111111111111111111111111111111111111111111111111111111111111111");
assert (uIntToStr(0xFF00000FF00000FF, 2, result)); assert (uIntToStr(0xFF00000FF00000FF, 2, result));
assert (result == "1111111100000000000000000000111111110000000000000000000011111111"); assert (result == "1111111100000000000000000000111111110000000000000000000011111111");