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,該怎么處理?

請讀者自己去研究解決。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM