最推荐使用 std::stod,它自动处理空格、符号、科学计数法并抛出明确异常;std::strtod 更底层但需手动判错;应避免 atof 和 std::Stringstream。

直接用 std::stod 是最安全、最推荐的方式,c++11 起已标准化,能自动处理空格、符号、科学计数法,并抛出明确异常。
用 std::stod 转换(推荐)
这是 C++ 标准库提供的首选方法,底层调用 strtod 但封装更友好,支持异常控制和起始位置返回。
- 输入字符串开头可含空格或正负号,例如
" -123.45e2"可正确解析 - 遇到非法字符时,只转换前面合法部分,
idx输出参数会指出第一个未转换字符的位置 - 若整个字符串无效(如
"abc"或空串),抛出std::invalid_argument - 若数值溢出(超
double表示范围),抛出std::out_of_range
std::string s = " 3.14159e-2"; size_t idx; try { double d = std::stod(s, &idx); if (idx != s.length()) { // 注意:s 中还有未解析的尾部,比如 "1.23abc" → d=1.23,idx=3 } } catch (const std::invalid_argument&) { // 格式错误,如 "xyz" } catch (const std::out_of_range&) { // 溢出,如 "1e309" }
用 std::strtod(C 风格,需手动判错)
比 stod 更底层,不抛异常,靠返回值和 endptr 判断结果,适合嵌入式或禁用异常的环境。
- 必须检查
endptr是否移动 —— 若等于输入指针,说明一个字符都没转 - 需手动判断是否溢出:
errno == ERANGE,且返回值为HUGE_VAL或-HUGE_VAL - 不跳过首尾空格以外的内容,比如
"123 456"只转123,endptr指向空格
std::string s = "1.7976931348623157e308extra"; char* endptr; errno = 0; double d = std::strtod(s.c_str(), &endptr); if (endptr == s.c_str()) { // 完全没识别到数字 } else if (*endptr != ' ') { // 有剩余未解析内容,如上面的 "extra" } else if (errno == ERANGE && (d == HUGE_VAL || d == -HUGE_VAL)) { // 溢出 }
避免用 atof 或 std::stringstream
atof 完全不报错,非法输入一律返回 0.0,无法区分 "0" 和 "garbage";std::stringstream 性能差、语法冗长,且失败时需手动检查 failbit,容易漏判。
立即学习“C++免费学习笔记(深入)”;
-
atof("xyz")返回0.0,无任何提示 -
std::stringstream ss("123.45"); double d; ss >> d;后必须写if (ss.fail() || !ss.EOF())才算完整,否则"123abc"会被静默接受为123.0
注意 locale 影响(小数点分隔符)
std::stod 和 std::strtod 默认使用 C locale,即小数点必须是英文句点 .。如果系统 locale 被设为德语(小数点用逗号 ,),它们仍按 C locale 解析,不会自动适配。
- 想支持本地化格式(如
"123,45"),不能依赖标准转换函数,需先替换逗号为点,或用std::use_facet<:num_get>>手动解析 - 跨平台分发时,尤其要注意 CI 环境或容器中 locale 是否被意外修改
真正难的不是选哪个函数,而是统一处理“部分有效”场景 —— 比如用户输入 "2.5px" 或 "-inf",是否接受?是否截断?这些逻辑必须在调用后立刻用 idx 或 endptr 显式检查,否则 bug 会藏得很深。