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 2c6a74c4f5
commit 4e85aa4df2
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,
/// 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;
}

View File

@ -35,6 +35,7 @@
#include <locale>
#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<double>::digits10,
int highDec = std::numeric_limits<double>::digits10);
int lowDec = -std::numeric_limits<float>::digits10,
int highDec = std::numeric_limits<float>::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 successful, false otherwise.
//
// end double-conversion functions declarations
//
} // 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)
{
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));
}

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)
{
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<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)
{
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))

View File

@ -16,6 +16,7 @@
#include "Poco/NumberFormatter.h"
#include <sstream>
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<double>(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");
}

View File

@ -930,7 +930,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");