有效地,没有科学记数或尾随零的双字符串

tpa*_*ale 11 c++ stringstream

这个例程被称为无数次,以创建充满数字的大型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"不一定是整个文件,它可以一次只能是一行或"段落",但任何避免大量内存分配的东西都是潜在的性能提升.


BSa*_*ita 5

在速度或简洁性方面高效?

char buf[64];
sprintf(buf, "%-.*G", 16, 1.0);
cout << buf << endl;
Run Code Online (Sandbox Code Playgroud)

显示“ 1”。在转换为科学计数法之前,最多可格式化16位有效数字,且不带尾随零。