C++11 std::to_string(double) - 没有尾随零

81

今天我尝试了一些C++11 STL的新函数,并遇到了std::to_string

这是一组非常好用的函数。以前,为了将double转换为string类型,需要创建一个stringstream对象,这似乎有些过度,现在我们可以像这样做:

std::cout << std::to_string(0.33) << std::endl;

结果是什么?

0.330000
我并不完全满意。有没有办法告诉std:: to_string去掉尾随零?我在互联网上搜索过,但据我所知该函数只接受一个参数(要转换的值)。在“旧日子”里,使用字符串流可以设置流的宽度,但我不想再进行转换。
有人遇到过这个问题/有解决方法吗?一些StackOverflow搜索无果。
(一个C++11 STL参考:http://en.cppreference.com/w/cpp/string/basic_string/to_string

@chris:不要在调用std::to_string之后。它返回一个字符串,而不是一个数字,并且其格式似乎很难修改。 - rubenvb
@rubenvb,糟糕,我没有好好思考,对吧... - chris
3
这是需要翻译的内容:"Lovely, lovely set of functions. Creating a stringstream object for just one double-to-string conversion always seemed overkill to me". And then you see why it isn't really overkill -- it's streams that contain all the infrastructure for specifying the format. To get what you want, to_string would have to duplicate all that in an equivalent API. Which might not be a terrible idea, but it's not the idea that the committee went with. The reason to_string is lovely is that it has no options ;-)可爱、可爱的函数组。对于只进行一次从双精度浮点数到字符串的转换,创建一个stringstream对象似乎有些过度。然而,接下来您会看到它并不是过度——流包含了指定格式的所有基础设施。要得到您想要的内容,to_string将不得不在一个等效的API中复制所有这些内容。这可能不是个坏主意,但这不是委员会采用的想法。to_string之所以可爱,是因为它没有选项;-) - Steve Jessop
1
@Steve:是的,我能理解他们的想法。我只是希望to_string(double/float)默认情况下不要附加任何尾随零,并且如果需要,可以使用stringstreams。但这已经涉及到个人偏好的问题了。 - Stijn Frishert
1
@Frishert:是的。我认为这些函数是快速且不太精确的输出器,用于记录或旨在被机器消耗的输出。不幸的是,对于漂亮的格式化,你通常必须自己动手。 - GManNickG
11个回答

75

如果你只想删除尾随的零,那很容易。

std::string str = std::to_string (f);
str.erase ( str.find_last_not_of('0') + 1, std::string::npos );
str.erase ( str.find_last_not_of('.') + 1, std::string::npos );

12
除了浮点数/双精度数是整数的这种特殊情况外,它几乎完美无缺。结果可能是例如 440.,但有些人如果想要漂亮的输出,可能更喜欢 440.0。不过,只需要检查最后一个字符就可以解决这个问题。 - Stijn Frishert
11
正确,这就是我为什么调整了马歇尔的代码的原因:`namespace util { template <typename T> std::string to_string(const T& t) { std::string str{std::to_string (t)}; int offset{1}; if (str.find_last_not_of('0') == str.find('.')) { offset = 0; } str.erase(str.find_last_not_of('0') + offset, std::string::npos);return str;} }`这也去除了一个不必要的小数点。 - antibus
6
. 重复最后一次删除即可去除尾部的句点。 str.erase ( str.find_last_not_of('0') + 1, std::string::npos ); str.erase ( str.find_last_not_of('.') + 1, std::string::npos ); - Zachary Canann
是的,就像@ZacharyCanann说的那样!我最终得到了很多以“.”结尾的数字 :) - thang

44
C++11标准明确指出(21.5/7):

返回值:每个函数都返回一个字符串对象,该对象保存其参数的字符表示形式。使用格式说明符"%d"、"%u"、"%ld"、"%lu"、"%lld"、"%llu"、"%f"、"%f"或"%Lf"调用sprintf(buf、fmt、val),其中buf指定具有足够大小的内部字符缓冲区。

这些函数按照如下顺序声明:

string to_string(int val);
string to_string(unsigned val);
string to_string(long val);
string to_string(unsigned long val);
string to_string(long long val);
string to_string(unsigned long long val);
string to_string(float val);
string to_string(double val);
string to_string(long double val);

因此,您无法控制生成字符串的格式。


22
为了去掉末尾的零:
std::ostringstream oss;
oss << std::setprecision(8) << std::noshowpoint << double_value;
std::string str = oss.str();
注意: #include <sstream>#include <iomanip>

6
我不明白为什么这个答案不是最好的!谢谢Ivan。 - Guillaume.P
1
很好的回答。只有一个问题,为什么需要将精度设置为8? - intrigued_66
2
@Guillaume.P 可能是因为它不是通用的。例如,如果您使用std::fixed,则showpoint没有预期的效果。 - zer0hedge

16

std::to_string无法控制格式,您会得到与适当类型的sprintf相同的结果 ("%f" 在这种情况下)。

如果您需要更多灵活性,那么您将需要一个更灵活的格式化程序 - 例如std::stringstream


1
好的,显然那不是我期望的答案,但感谢你澄清了这个问题 :) - Stijn Frishert

5

随着C++20的到来,我们终于可以拥抱std::format的强大功能了!希望GCC和Clang也能尽快跟进MSVC,实现这个特性。

std::cout << std::format("{}\n", 0.33);

2

std::to_string(double)是由标准定义的,它只返回与sprintf(buf, "%f", value)生成的字符序列相同的序列。没有更多、没有更少,特别是没有办法调整格式说明符。所以,不,你什么也做不了。


1

0
一个解决问题的多样化方案,因为to_string无法工作。 “魔法公式”- 我的CS2400老师
std::cout.setf(ios::fixed);
std::cout.setf(ios::showpoint);
std::cout.precision(2);

const double x = 0.33, y = 42.3748;
std::cout << "$" << x << std::endl;
std::cout << "$" << y << std::endl;

输出:

$0.33
$42.37

任何使用十进制数的后续输出都将被设置为如此。
您可以随时根据需要更改setf和precision。

0
创建一个自定义转换函数,必要时删除尾随零。
//! 2018.05.14 13:19:20 CST
#include <string>
#include <sstream>
#include <iostream>
using namespace std;

//! Create a custom convert function, remove the tailing zeros if necessary.  
template<typename T>
std::string tostring(const T &n) {
    std::ostringstream oss;
    oss << n;
    string s =  oss.str();
    int dotpos = s.find_first_of('.');
    if(dotpos!=std::string::npos){
        int ipos = s.size()-1;
        while(s[ipos]=='0' && ipos>dotpos){
            --ipos;
        }
        s.erase ( ipos + 1, std::string::npos );
    }
    return s;
}

int main(){
    std::cout<< tostring(1230)<<endl;
    std::cout<< tostring(12.30)<<endl;
}

输入的数字:

1230
12.30

使用-std=c++11编译,然后得到以下结果:

1230
12.3

0
编写一个通用的辅助函数,如下所示,您可以在C++项目中重新利用它来满足舍入需求。
inline static const string roundDouble(const double input, const int decimal_places)
{
    ostringstream str;
    str << fixed << setprecision(decimal_places);
    str << input;
    return str.str();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接