这个例程被称为无数次,以创建充满数字的大型csv文件.有没有更有效的方法来实现这一目标?
static std::string dbl2str(double d)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(10) << d; //convert double to string w fixed notation, hi precision
std::string s = ss.str(); //output to std::string
s.erase(s.find_last_not_of('0') + 1, std::string::npos); //remove trailing 000s (123.1200 => 123.12, 123.000 => 123.)
return (s[s.size()-1] == '.') ? s.substr(0, s.size()-1) : s; //remove dangling decimal (123. => 123)
}
Run Code Online (Sandbox Code Playgroud)
Ste*_*sop 10
在开始之前,请检查此功能是否花费了大量时间.通过测量仪或其他方法进行测量.知道你称之为无数次是非常好的,但是如果事实证明你的程序仍然只花费1%的时间在这个函数上,那么你在这里做的任何事情都不可能使你的程序性能提高1%以上.如果是这种情况,你的问题的答案将是"为了你的目的不,这个功能不能显着提高效率,如果你尝试,你就是在浪费时间".
首先,避免s.substr(0, s.size()-1).这复制了大部分字符串,它使你的函数不符合NRVO的条件,所以我认为你通常会在返回时获得一份副本.所以我做的第一个改变是用以下代码替换最后一行:
if(s[s.size()-1] == '.') {
s.erase(s.end()-1);
}
return s;
Run Code Online (Sandbox Code Playgroud)
但如果表现是一个严重的问题,那么我就是这样做的.我不承诺这是最快的,但它避免了一些不必要的分配和复制的问题.任何涉及的stringstream方法都需要从字符串流到结果的副本,因此我们需要更低级别的操作snprintf.
static std::string dbl2str(double d)
{
size_t len = std::snprintf(0, 0, "%.10f", d);
std::string s(len+1, 0);
// technically non-portable, see below
std::snprintf(&s[0], len+1, "%.10f", d);
// remove nul terminator
s.pop_back();
// remove trailing zeros
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
// remove trailing point
if(s.back() == '.') {
s.pop_back();
}
return s;
}
Run Code Online (Sandbox Code Playgroud)
第二次调用snprintf假定std::string使用连续存储.这在C++ 11中得到了保证.它不能保证在C++ 03中,但对于std::stringC++委员会已知的所有主动维护的实现都是如此.如果性能真的很重要,那么我认为做出那种不可移植的假设是合理的,因为直接写入字符串会在以后将复制保存到字符串中.
s.pop_back()是C++ 11的说法s.erase(s.end()-1),而且s.back()是s[s.size()-1]
对于另一个可能的改进,你可以摆脱第一次调用snprintf,而是将你的大小调整s为某些值std::numeric_limits<double>::max_exponent10 + 14(基本上,-DBL_MAX需要的长度).麻烦的是,这会分配和存储比通常需要的内存更多的内存(IEEE双字节为322字节).我的直觉是,这将比第一次调用慢snprintf,更不用说在字符串返回值被调用者暂停一段时间的情况下浪费内存.但你总能测试它.
或者,std::max((int)std::log10(d), 0) + 14计算所需大小的合理上限,并且可能比snprintf准确计算它更快.
最后,您可以通过更改功能界面来提高性能.例如,您可以附加到调用者传入的字符串,而不是返回一个新字符串:
void append_dbl2str(std::string &s, double d) {
size_t len = std::snprintf(0, 0, "%.10f", d);
size_t oldsize = s.size();
s.resize(oldsize + len + 1);
// technically non-portable
std::snprintf(&s[oldsize], len+1, "%.10f", d);
// remove nul terminator
s.pop_back();
// remove trailing zeros
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
// remove trailing point
if(s.back() == '.') {
s.pop_back();
}
}
Run Code Online (Sandbox Code Playgroud)
然后调用者可以有reserve()足够的空间,多次调用你的函数(可能在其间插入其他字符串),并将得到的数据块一次性写入文件,除了reserve.之外没有任何内存分配."Plenty"不一定是整个文件,它可以一次只能是一行或"段落",但任何避免大量内存分配的东西都是潜在的性能提升.
在速度或简洁性方面高效?
char buf[64];
sprintf(buf, "%-.*G", 16, 1.0);
cout << buf << endl;
Run Code Online (Sandbox Code Playgroud)
显示“ 1”。在转换为科学计数法之前,最多可格式化16位有效数字,且不带尾随零。
| 归档时间: |
|
| 查看次数: |
6640 次 |
| 最近记录: |