一倍,达到字符串没有科学记数法或尾随零,有效地(double to string without s

2019-07-20 19:34发布

这个程序被称为无数次创造全数字大型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)
    }

Answer 1:

在开始之前,先检查本功能显著时间是否都花在。 通过测量做到这一点,无论是与探查或以其他方式。 知道你叫它亿万倍都非常好,但如果事实证明你的程序仍然只花费其时间的1%这个功能,那么你在这里做都不可能提高超过1%,你的程序的性能无关。 如果是这样的情况下,回答你的问题是“你的目的没有,这个功能不能显著更有效地做,你是在浪费你的时间,如果你尝试”。

第一件事情,避免s.substr(0, s.size()-1) 这大部分的字符串的副本它使你的函数没有资格NRVO,所以我觉得一般,你会在返回得到一份拷贝。 所以我会做的第一个变化是,以取代最后一行:

if(s[s.size()-1] == '.') {
    s.erase(s.end()-1);
}
return s;

但是,如果性能是一个严重的问题,那么这里就是我会做。 我不看好,这是最快的可能,但它避免了不必要的分配和复制一些问题。 任何方法都涉及到stringstream将需要从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;
}

第二个呼叫snprintf假定std::string使用连续的存储。 这在C ++ 11保证。 它不能保证在C ++ 03,但真正为所有积极维护实现std::string已知C ++委员会。 如果性能真的是非常重要的话,我认为这是合理的,使非便携式的假设,因为直接写入到一个字符串节省复制到一个字符串后面。

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需要)。 麻烦的是,该分配和零点远更多的内存比通常需要(322个字节用于IEEE双)。 我的直觉是,这会比第一次调用速度较慢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();
    }
}

然后调用者可以reserve()足够的空间,调用函数几次(推测可能与其他字符串之间的追加),并将得到的数据块一次全部写入文件,而不是其他任何内存分配reserve 。 “大量”不必是整个文件,也可能是一条线或“款”的时间,但任何避免了数不胜数的内存分配是一个潜在的性能提升。



Answer 2:

高效的速度和简洁的条款?

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

显示 “1”。 格式,最高可达显著16位,没有尾随零,恢复到科学计数法之前。



Answer 3:

  • 使用snprintf和阵列char代替stringstreamstring
  • 一个指针传递给char缓冲到dbl2str到其中它打印(以避免拷贝构造string返回时调用)。 组装在一个字符缓冲区要打印的字符串(或转换字符缓存器调用时将字符串或将其添加到现有的字符串)
  • 声明函数inline在头文件

     #include <cstdio> inline void dbl2str(char *buffer, int bufsize, double d) { /** the caller must make sure that there is enough memory allocated for buffer */ int len = snprintf(buffer, bufsize, "%lf", d); /* len is the number of characters put into the buffer excluding the trailing \0 so buffer[len] is the \0 and buffer[len-1] is the last 'visible' character */ while (len >= 1 && buffer[len-1] == '0') --len; /* terminate the string where the last '0' character was or overwrite the existing 0 if there was no '0' */ buffer[len] = 0; /* check for a trailing decimal point */ if (len >= 1 && buffer[len-1] == '.') buffer[len-1] = 0; } 


文章来源: double to string without scientific notation or trailing zeros, efficiently