C++11轉化double爲string是一件很容易的事情。ios
1:使用C中的sprintf函數,這裏就不說了。git
2:使用std::ostringstream。這個與std::cout是同樣的。這個在C++11之前就已經支持了的。這個得出的結果與使用std::cout的結果是同樣的。ide
3:從C++11開始,標準庫提供了std::to_string輔助函數轉化各種型爲一個字符串。函數
可是std::ostringstream和std::to_string使用的結果就有些很奇怪的差別了。主要有:性能
1:std::string獲得的結果始終是小數位必然是6!測試
2:默認狀況下,std::ostringstream對double使用的是6位精度。這裏精度值的是整數位和小數位個數和。可是精度是能夠設置的。這裏統一談論默認的狀況。spa
也就是說,若是參數精度超過6位的話,那麼會將數值四捨五入,而後丟棄多餘的位數!
具體來講是這樣的:code
1:若是整數位不足6位,而總體精度超過了6,那麼小數位四捨五入,而後截斷多餘的位數。blog
2:若是是整數位超過了6,那麼捨棄小數位,而後按照科學計數法保存。ci
好比:
序號 | 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 11 ▫ 13 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 18 ▫ 20 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 25 ▫ 27 26 assert( val == expect_str); 28 27 } 29 28 ▫ 30 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 35 ▫ 37 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 }
咱們這裏將結果整理出來以下表
序號 | 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 }
這是測試結果的輸出:
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 }
這是測試結果的輸出:
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 }
1:實際上第三版的實現存在一個BUG,即設置默認小數位後沒有執行四捨五入!
2:性能。這個實現性能如何,是否是存在更佳的實現呢?
3:處理完成後,若是小數位全是0,該怎麼處理?
請讀者本身去研究解決。