将 double 插入到 stringstream 时避免舍入
Avoid rounding when inserting double to stringstream
我正在使用此函数将双精度 f
转换为小数点后 p
位的字符串。
static std::string doubleToString(double f, int p)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(p) << f;
std::string s = ss.str();
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
return (s[s.size()-1] == '.') ? s.substr(0, s.size()-1) : s;
}
末尾的剩余 0 被擦除。例如 1.23000
得到 1.23
。
输入:
double test1 = 1;
double test2 = 1.12345678;
double test3 = 1.123456789010;
std::cout << doubleToString(test1, 8) << " " << doubleToString(test2, 8) << " " << doubleToString(test3, 8);
输出:
1 1.12345678 1.12345679
如您所见,test1
和 test2
打印正常。 test3
但是在第 8 位数字后四舍五入。我想避免那个和 "cut" 其余数字,因此:doubleToString(1.123456789010, 8)
returns 1.12345678
。
最有效的方法是什么?
你可以试试:
doubleToString( ( test3 - ( 0.5 / ( pow( 10, 8 ) ) ) ), 8 )
这不是一个明确的解决方案,但应该可以满足您的需求。
编辑:
它只是从您的数字中减去 0.000000005,因此不会四舍五入。您可以将它添加到您的函数中,它将对每个数字执行此操作:
static std::string doubleToString(double f, int p)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(p) << (f - (0.5 / (pow(10, p))));
std::string s = ss.str();
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
return (s[s.size()-1] == '.') ? s.substr(0, s.size()-1) : s;
}
对您的功能稍作修改即可完成工作;只需将一些额外的数字放入流中,并在转换后将其删除。您需要允许 some 舍入,因为二进制值可能略小于您期望的十进制值。在我下面的代码中,我将字符串转换限制为 14 位数字,这大约是您可以从 1.0 到 10.0 之间的 IEEE 双精度数期望的最大值;如果您期望值大于此值,则需要调整算法,因为小数点后的准确位数会减少。
std::string doubleToString(double f, int p)
{
std::stringstream ss;
int p2 = min(p + 2, 14);
ss << std::fixed << std::setprecision(p2) << f;
std::string s = ss.str();
size_t point = s.find('.');
if (point != std::string::npos && point + 1 + p < s.size())
s.erase(point + 1 + p);
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
return (s[s.size()-1] == '.') ? s.substr(0, s.size()-1) : s;
}
在 http://ideone.com/xGoDiH 查看它的运行情况。
我正在使用此函数将双精度 f
转换为小数点后 p
位的字符串。
static std::string doubleToString(double f, int p)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(p) << f;
std::string s = ss.str();
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
return (s[s.size()-1] == '.') ? s.substr(0, s.size()-1) : s;
}
末尾的剩余 0 被擦除。例如 1.23000
得到 1.23
。
输入:
double test1 = 1;
double test2 = 1.12345678;
double test3 = 1.123456789010;
std::cout << doubleToString(test1, 8) << " " << doubleToString(test2, 8) << " " << doubleToString(test3, 8);
输出:
1 1.12345678 1.12345679
如您所见,test1
和 test2
打印正常。 test3
但是在第 8 位数字后四舍五入。我想避免那个和 "cut" 其余数字,因此:doubleToString(1.123456789010, 8)
returns 1.12345678
。
最有效的方法是什么?
你可以试试:
doubleToString( ( test3 - ( 0.5 / ( pow( 10, 8 ) ) ) ), 8 )
这不是一个明确的解决方案,但应该可以满足您的需求。
编辑: 它只是从您的数字中减去 0.000000005,因此不会四舍五入。您可以将它添加到您的函数中,它将对每个数字执行此操作:
static std::string doubleToString(double f, int p)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(p) << (f - (0.5 / (pow(10, p))));
std::string s = ss.str();
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
return (s[s.size()-1] == '.') ? s.substr(0, s.size()-1) : s;
}
对您的功能稍作修改即可完成工作;只需将一些额外的数字放入流中,并在转换后将其删除。您需要允许 some 舍入,因为二进制值可能略小于您期望的十进制值。在我下面的代码中,我将字符串转换限制为 14 位数字,这大约是您可以从 1.0 到 10.0 之间的 IEEE 双精度数期望的最大值;如果您期望值大于此值,则需要调整算法,因为小数点后的准确位数会减少。
std::string doubleToString(double f, int p)
{
std::stringstream ss;
int p2 = min(p + 2, 14);
ss << std::fixed << std::setprecision(p2) << f;
std::string s = ss.str();
size_t point = s.find('.');
if (point != std::string::npos && point + 1 + p < s.size())
s.erase(point + 1 + p);
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
return (s[s.size()-1] == '.') ? s.substr(0, s.size()-1) : s;
}
在 http://ideone.com/xGoDiH 查看它的运行情况。