• C++11 double转化为string


    C++11转化double为string是一件很容易的事情。

    方法:

    1:使用C中的sprintf函数,这里就不说了。

    2:使用std::ostringstream。这个与std::cout是一样的。这个在C++11以前就已经支持了的。这个得出的结果与使用std::cout的结果是一样的。

    3:从C++11开始,标准库提供了std::to_string辅助函数转化各类型为一个字符串。

    std::ostringstream和std::to_string

    但是std::ostringstream和std::to_string使用的结果就有些很奇怪的差异了。主要有:

    1:std::string得到的结果始终是小数位必然是6!

    2:默认情况下,std::ostringstream对double使用的是6位精度。这里精度值的是整数位和小数位个数和。但是精度是可以设置的。这里统一谈论默认的情况。

    也就是说,如果参数精度超过6位的话,那么会将数值四舍五入,然后丢弃多余的位数!
    具体来说是这样的:

    1:如果整数位不足6位,而整体精度超过了6,那么小数位四舍五入,然后截断多余的位数。

    2:如果是整数位超过了6,那么舍弃小数位,然后按照科学计数法保存。

    比如:

    序号 double原值 std::ostringstream结果
    1 1.0000000000001 1
    2 0.12345678 0.123457
    3 123456789.0000000000001 1.23457e+08
    4 123456789.1234567 1.23457e+08
    5 0.0000000000001 1e-13

    下面我们详细比较std::ostringstream和std::to_string使用的结果的差异。

    这里是详细的测试代码,请注意,这里必须是C++11及以上版本!

     1     1 #include <string>                                                                                                                                                                                                                                                                                                      
     2     2 #include <cassert>
     3     3 #include <iostream>
     4     4 #include <sstream>
     5     5
     6     6 std::string DoubleToStringByStdToString(double value)
     7     7 {
     8 
     9     8     const std::string& new_val = std::to_string(value);
    10     9     return new_val;
    11    10  }
    12    1113    12  std::string DoubleToStringByStringStream(double value)
    14    13  {
    15    14      std::ostringstream stream;
    16    15      stream << value;
    17    16      return stream.str();
    18    17  }
    19    1820    19  void TestDoubleToStringByStdToString(const double value, const std::string& origin, const std::string& expect_str)
    21    20  {
    22    21      const std::string& val = DoubleToStringByStdToString(value);
    23    22      std::cout << __FUNCTION__ << " --> original:" << origin
    24    23                                << ", std::cout:" << value
    25    24                                << ", std::to_string:" << val<< std::endl;
    26    2527    26      assert( val == expect_str);
    28    27  }
    29    2830    29  void TestDoubleToStringByStringStream(const double value, const std::string& origin, const std::string& expect_str)
    31    30  {
    32    31      const std::string& val = DoubleToStringByStringStream(value);
    33    32      std::cout << __FUNCTION__ << " --> original:" << origin
    34    33                                << ", std::cout:" << value
    35    34                                << ", std::stringstream:" << val<< std::endl;
    36    3537    36      assert( val == expect_str);
    38    37  }
    39    38
    40    39 int main(int argc, char* argv[])
    41    40 {
    42    41     TestDoubleToStringByStdToString(0, "0", "0.000000");
    43    42     TestDoubleToStringByStringStream(0, "0", "0");
    44    43
    45    44     TestDoubleToStringByStdToString(.0, ".0", "0.000000");
    46    45     TestDoubleToStringByStringStream(.0, ".0", "0");
    47    46
    48    47     TestDoubleToStringByStdToString(0.0, "0.0", "0.000000");
    49    48     TestDoubleToStringByStringStream(0.0, "0.0", "0");
    50    49
    51    50     TestDoubleToStringByStdToString(1.0, "1.0", "1.000000");
    52    51     TestDoubleToStringByStringStream(1.0, "1.0", "1");
    53    52
    54    53     TestDoubleToStringByStdToString(1.0000008, "1.0000008", "1.000001");
    55    54     TestDoubleToStringByStringStream(1.0000008, "1.0000008", "1");
    56    55
    57    56     TestDoubleToStringByStdToString(1.0000000000001,"1.0000000000001", "1.000000");
    58    57     TestDoubleToStringByStringStream(1.0000000000001,"1.0000000000001", "1");
    59    58
    60    59     TestDoubleToStringByStdToString(0.0000000000001,"0.0000000000001", "0.000000");
    61    60     TestDoubleToStringByStringStream(0.0000000000001,"0.0000000000001", "1e-13");
    62    61
    63    62     TestDoubleToStringByStdToString(0.12345678,"0.12345678", "0.123457");
    64    63     TestDoubleToStringByStringStream(0.12345678,"0.12345678", "0.123457");
    65    64
    66    65     TestDoubleToStringByStdToString(100000000000.0000000000001,"100000000000.0000000000001", "100000000000.000000");
    67    66     TestDoubleToStringByStringStream(100000000000.0000000000001,"100000000000.0000000000001", "1e+11");
    68    67
    69    68     TestDoubleToStringByStdToString(1e+11,"1e+11",  "100000000000.000000");
    70    69     TestDoubleToStringByStringStream(1e+11,"1e+11",  "1e+11");
    71    70
    72    71     TestDoubleToStringByStdToString(123456.0000000000001, "123456.0000000000001", "123456.000000");
    73    72     TestDoubleToStringByStringStream(123456.0000000000001, "123456.0000000000001", "123456");
    74    73
    75    74     TestDoubleToStringByStdToString(123456789.1234567,"123456789.1234567", "123456789.123457");
    76    75     TestDoubleToStringByStringStream(123456789.1234567,"123456789.1234567", "1.23457e+08");
    77    76
    78    77     TestDoubleToStringByStdToString(123456789.0000000000001,"123456789.0000000000001", "123456789.000000");
    79    78     TestDoubleToStringByStringStream(123456789.0000000000001,"123456789.0000000000001", "1.23457e+08");
    80    79
    81    80     return 0;
    82    81 }
    View Code

    我们这里将结果整理出来如下表

    序号 double原值 std::cout std::ostringstream结果 std::to_string()结果
    1 0 0 0 0.000000
    2 .0 0 0 0.000000
    3 0.0 0 0 0.000000
    4 1.0 1 1 1.000000
    5 1.0000008 1 1 1.000001
    6 1.0000000000001 1 1 1.000000
    7 0.0000000000001 1e-13 1e-13 0.000000
    8 0.12345678 0.123457 0.123457 0.123457
    9 100000000000.0000000000001 1e+11 1e+11 100000000000.000000
    10 1e+11 1e+11 1e+11 100000000000.000000
    11 123456.0000000000001 123456 123456 123456.000000
    12 123456789.1234567 1.23457e+08 1.23457e+08 123456789.123457
    13 123456789.0000000000001 1.23457e+08 1.23457e+08 123456789.000000

    从上面的结果我们还可以发现一个关于std::to_string的特点

    如果传入的double本身是科学记数法,to_string仍然可以执行转化,且得出的结果与该科学技术法表述的值转化的结果是一样的!

    总结

    虽然C++对关转化double为string提供的方法很多,但是的得出的结果不一样。所以在使用时应该统一方法,并且格外小心,如果是在对double很敏感的行业,那么应该对该操作封装,并提供足够的控制参数。

    一种可参考的实现

    从上面我们可以看出使用ostringstream或者to_string的方法,要么存在精度显示问题要么调整为科学计数法显示。这些都不是我们想要的。

    所以我们可以使用ostringstream封装一个辅助函数,可以控制精度也可以控制科学计数法显示。

    精度

    ostringstream是可以控制精度的,函数原型如下:

    std::ios_base::precision

    第一版

     1 std::string DoubleToString(const double value, unsigned int precision)                                                                                                                                                                                                          
     2 {
     3    std::ostringstream out;
     4    if (precision > 0)
     5        out.precision(precision);
     6 
     7    out << value;
     8    return out.str();
     9 }
    10 
    11 
    12 int main(int argc, char* argv[])
    13 {
    14     std::cout << DoubleToString(0., 12) << std::endl;
    15     std::cout << DoubleToString(0.0, 12) << std::endl;
    16     std::cout << DoubleToString(.0, 12) << std::endl;
    17     std::cout << DoubleToString(1.0, 12) << std::endl;
    18     std::cout << DoubleToString(11234, 12) << std::endl;
    19     std::cout << DoubleToString(0.12345, 12) << std::endl;
    20     std::cout << DoubleToString(0.12345678, 12) << std::endl;
    21     std::cout << DoubleToString(0.12345678, 9) << std::endl;
    22     std::cout << DoubleToString(0.12345678, 8) << std::endl;
    23     std::cout << DoubleToString(0.12345678, 6) << std::endl;
    24     return 0;
    25 }
    View Code

    这是测试结果的输出:

    0
    0
    0
    1
    11234
    0.12345
    0.12345678
    0.12345678
    0.12345678
    0.123457

    第二版

    多数情况下我们更加关注的是小数点后的几位数,所以我们调整参数控制小数点后位数。

     1 #include <limits>
     2 std::string DoubleToString(const double value, unsigned int precisionAfterPoint)
     3 {
     4    std::ostringstream out;
     5    // 清除默认精度
     6    out.precision(std::numeric_limits<double>::digits10);
     7    out << value;
     8 
     9    std::string res = std::move(out.str());
    10    auto pos = res.find('.');
    11    if (pos == std::string::npos)
    12        return res;
    13 
    14    auto splitLen = pos + 1 + precisionAfterPoint;
    15    if (res.size() <= splitLen)
    16        return res;
    17 
    18    return res.substr(0, splitLen);
    19 }
    20 
    21 int main(int argc, char* argv[])
    22 {
    23     std::cout << DoubleToString(0., 12) << std::endl;
    24     std::cout << DoubleToString(0.0, 12) << std::endl;
    25     std::cout << DoubleToString(.0, 12) << std::endl;
    26     std::cout << DoubleToString(1.0, 12) << std::endl;
    27     std::cout << DoubleToString(11234, 12) << std::endl;
    28     std::cout << DoubleToString(12345.12345678, 12) << std::endl;
    29     std::cout << DoubleToString(12345.12345678, 9) << std::endl;
    30     std::cout << DoubleToString(12345.12345678, 7) << std::endl;
    31     std::cout << DoubleToString(12345.12345678, 8) << std::endl;
    32     std::cout << DoubleToString(12345.12345678, 6) << std::endl;
    33     std::cout << DoubleToString(12345.00000001, 7) << std::endl;
    34     std::cout << DoubleToString(12345.00000001, 8) << std::endl;
    35     std::cout << DoubleToString(12345.00000001, 6) << std::endl;
    36     return 0;
    37 }
    View Code

    这是测试结果的输出:

    0
    0
    0
    1
    11234
    12345.12345678
    12345.12345678
    12345.1234567
    12345.12345678
    12345.123456
    12345.0000000
    12345.00000001
    12345.000000

    第三版

    更进一步的,我们一般默认情况下小数点后是是6位小数的。所以我们可以设置默认参数:

     1 #include <limits>
     2 std::string DoubleToString(const double value, unsigned int precisionAfterPoint = 6)
     3 {
     4    std::ostringstream out;
     5    // 清除默认精度
     6    out.precision(std::numeric_limits<double>::digits10);
     7    out << value;
     8 
     9    std::string res = std::move(out.str());
    10    auto pos = res.find('.');
    11    if (pos == std::string::npos)
    12        return res;
    13 
    14    auto splitLen = pos + 1 + precisionAfterPoint;
    15    if (res.size() <= splitLen)
    16        return res;
    17 
    18    return res.substr(0, splitLen);
    19 }
    View Code

    第四版

    1:实际上第三版的实现存在一个BUG,即设置默认小数位后没有执行四舍五入!

    2:性能。这个实现性能如何,是不是存在更佳的实现呢?

    3:处理完成后,如果小数位全是0,该怎么处理?

    请读者自己去研究解决。

  • 相关阅读:
    googleMapReduce
    leveldb0
    大端模式和小端模式
    信号
    js中判断对象类型的几种方法
    js DOM之基础详解
    JavaScript作用域与闭包总结
    SCRIPT438: 对象不支持“trim”属性或方法
    JS合并多个数组去重算法
    js的 break 和 continue 计算问题
  • 原文地址:https://www.cnblogs.com/chorulex/p/7660187.html
Copyright © 2020-2023  润新知