C++ double 与 string 互转

在把浮点数转换成字符串时,不少初学 C++ 的小伙伴都会觉得奇怪。明明变量里保存的是 36.5,调用 std::to_string() 转换后,末尾却多出了一串 0,变成了 "36.500000"。

这是因为浮点数的文本转换通常还涉及小数位数的保留规则。在 C++ 中,我们既可以把 double 转为字符串并按需控制小数位数,也可以反过来把字符串解析为 double 数值。

double 转 string

将 double 浮点数转换为 std::string 字符串主要有以下 2 种方式,我们可以根据是否需要控制小数位数来选择。

1. 使用 std::to_string() 默认转换

如果我们只是想把浮点数快速转成字符串,对末尾的小数位数没有特别要求,直接调用 <string> 头文件中的 std::to_string() 函数即可。

语法:

std::to_string(浮点数)

说明:

std::to_string() 会将传入的浮点数转换为一个新的 std::string 并返回。除了 double 之外,它也支持 float 和 long double 类型。

示例 1:默认转换为 string

#include <iostream>
#include <string>

int main() {
    double temp = 36.5;
    std::string text = std::to_string(temp);

    std::cout << text << '\n';
    std::cout << "当前体温:" << text << "℃" << '\n';
    return 0;
}

运行结果如下。

36.500000
当前体温:36.500000℃

分析:

为什么 36.5 经过 std::to_string(temp) 转换后会变成 "36.500000" 呢?因为 std::to_string() 在转换浮点数时,底层规则跟 C 语言的 printf("%f") 相似,默认固定保留 6 位小数。

当小数不足 6 位时,它会在末尾自动补 0;当小数超过 6 位时,则会按四舍五入保留 6 位。比如 std::to_string(3.1415926) 会得到 "3.141593",而极小的 std::to_string(0.0000001) 则会变成 "0.000000"。

2. 转换时控制小数位数(推荐)

在实际开发中,像评分、体温、商品单价这类数据,转成字符串时通常只需要保留 1 位或 2 位小数,并不希望末尾拖着一串多余的 0。

由于 std::to_string() 不支持指定小数位数,我们可以借助 <sstream> 头文件中的 std::ostringstream(输出字符串流)。你可以把它看作 “内存里的 std::cout”:平时 std::cout 是把内容打印到屏幕上,而它是把内容写进内存字符串里。

写入浮点数时,我们只需要配合前面学过的 std::fixed 和 std::setprecision(),就能自由控制保留几位小数。

语法:

std::ostringstream 流名称;
流名称 << std::fixed << std::setprecision(位数) << 浮点数;
std::string 结果字符串 = 流名称.str();

说明:

其中,std::fixed 表示采用固定小数点格式,配合 <iomanip> 头文件中的 std::setprecision(位数),即可按四舍五入规则保留指定位数的小数。写入完成后,调用成员函数 str() 就能取出生成好的 std::string 字符串。

示例 2:转换时保留 2 位小数

#include <iomanip>                   // 提供 std::setprecision()
#include <iostream>
#include <sstream>                   // 提供 std::ostringstream
#include <string>

int main() {
    double score = 9.456;

    std::ostringstream out;
    out << std::fixed << std::setprecision(2) << score;

    std::string text = out.str();    // 取出格式化后的字符串
    std::cout << "最终得分:" << text << '\n';
    return 0;
}

运行结果如下。

最终得分:9.46

分析:

在这个例子中,我们将 9.456 写入字符串流 out,并通过 std::fixed 和 std::setprecision(2) 指定保留 2 位小数。四舍五入后,调用 out.str() 取出的结果就是 "9.46"。如果把 score 换成 9.4,末尾也会自动补齐为 "9.40"。

回到文章开头的疑问:对于 36.5 这样的日常小数,如果我们只想去掉末尾多余的 0 得到 "36.5",应该怎么写呢?其实只要不加 std::fixed,直接把数值写入字符串流就可以了:

std::ostringstream out;
out << 36.5;
std::string cleanStr = out.str();    // 得到 "36.5"

需要注意的是,不加 std::fixed 时流默认最多显示 6 位有效数字,遇到像 1234567.5 这样位数较多的数会自动变成科学计数法。关于 stringstream 字符串流的更多用法,后面很快就会学到。

提示 在 C++20 标准中,我们还可以使用 <format> 头文件提供的 std::format() 函数,直接写 std::format("{:.2f}", score) 就能保留 2 位小数生成字符串。

string 转 double:std::stod()

反过来,如果我们手里拿到的是浮点数字符串,希望解析成真正的 double 数值参与计算,可以使用 <string> 头文件中的 std::stod() 函数。其中,stod 是英文 “string to double(字符串转浮点数)” 的缩写。

1. 基本用法

我们将包含小数的 std::string 字符串传给 std::stod(),就能得到对应的 double 浮点数。它不仅支持普通小数,还支持像 "3.14e2" 这样的科学计数法字符串。

语法:

std::stod(字符串)

说明:

std::stod() 会自动跳过字符串开头的空格,然后将后面的浮点数文本解析为 double 并返回。

示例 3:字符串解析为 double

#include <iostream>
#include <string>

int main() {
    std::string text = "12.5";
    double num = std::stod(text);

    // 解析科学计数法字符串
    double sciVal = std::stod("3.14e2");

    std::cout << num << '\n';
    std::cout << num + 0.5 << '\n';
    std::cout << sciVal << '\n';
    return 0;
}

运行结果如下。

12.5
13
314

分析:

std::stod(text) 表示将字符串 "12.5" 解析为 double 数值 12.5,因此后面的 num + 0.5 执行的是浮点数加法,得到 13。另外,"3.14e2" 表示 3.14 乘以 10 的 2 次方,std::stod() 同样能识别并转出 314。

根据目标浮点数精度的不同,<string> 头文件中一共提供了以下 3 个转换函数,如下表所示。

字符串转浮点数函数
函数名 返回类型 说明
std::stof() float 转换为单精度浮点数
std::stod() double 最常用,转换为双精度浮点数
std::stold() long double 转换为扩展精度浮点数

2. 部分解析与异常边界

与前面学过的 std::stoi() 类似,std::stod() 在遇到 "19.8元"、"36.5℃" 这类末尾带有单位的字符串时,也会把开头的有效浮点数提取出来,并支持通过第 2 个参数 &pos 记录停止解析的字符下标。

示例 4:记录停止解析的位置

#include <iostream>
#include <string>

int main() {
    std::string priceText = "19.8元";
    std::size_t pos = 0;

    double price = std::stod(priceText, &pos);

    std::cout << "单价:" << price << '\n';
    std::cout << "停止下标:" << pos << '\n';
    return 0;
}

运行结果如下。

单价:19.8
停止下标:4

分析:

字符串 "19.8元" 开头的 19.8 连同小数点一共占了 4 个字符(下标 0 ~ 3),解析在汉字 "元" 所在的下标 4 处停下,因此变量 pos 的值为 4。

这里小伙伴们要留意符号出现的位置:"19.8元" 能够解析成功,是因为数字排在最开头。如果货币符号或文字出现在最前面,函数从第 1 个字符就无法识别出数字,下面这种写法在运行时就会直接抛出异常。

double badVal = std::stod("¥19.8");    // 异常:开头不是数字

具体来说,开头没有合法浮点数时会抛出 std::invalid_argument 异常,数值超出 double 范围(如 "1e500")时会抛出 std::out_of_range 异常。大家在处理外部输入时,记得配合 try...catch 捕获异常。

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号