yaml-cpp 0.8.0+ 默认将整数解析为 double,导致 int64_t 精度丢失;需手动特化 yaml::convert 并确保其定义在首次 as() 调用前生效,否则仍走 double 中转。

yaml-cpp 读取 int64_t 时默认转成 double 的坑
yaml-cpp(0.8.0+)默认把长整型数字(如 9223372036854775807)解析为 double,哪怕 YAML 文件里写的是整数形式。这不是 bug,是设计:它优先用浮点表示所有数字以兼容科学计数法,但代价是丢失 int64_t 精度(double 在 2⁵³ 以上无法精确表示整数)。你调用 as<int64_t>()</int64_t> 会触发隐式转换,可能溢出或截断。
正确启用 int64_t 支持需手动注册模板特化
yaml-cpp 不内置 int64_t 的序列化/反序列化逻辑,必须显式特化 YAML::convert<t></t>。否则 node.as<int64_t>()</int64_t> 会 fallback 到通用数字转换路径,走 double 中转。
- 在你的代码中(通常放在 .cpp 文件顶部或单独头文件),添加如下特化:
namespace YAML {
template
struct convert<int64_t> {
static Node encode(const int64_t& rhs) {
Node node;
node = static_cast<long long>(rhs); // 避免平台 long 大小歧义
return node;
}
static bool decode(const Node& node, int64_t& rhs) {
if (!node.IsScalar()) return false;
try {
// 先尝试整数解析(支持十进制、十六进制如 0xFF)
rhs = std::stoll(node.Scalar(), nullptr, 0);
return true;
} catch (const std::out_of_range&) {
return false;
} catch (const std::invalid_argument&) {
// fallback:尝试作为 double 解析再转(仅当确定不会精度丢失时)
if (node.IsScalar() && node.Scalar().find('.') == std::string::npos) {
try {
rhs = static_cast<int64_t>(std::stod(node.Scalar()));
return true;
} catch (...) {}
}
return false;
}
}
};
}</int64_t></long></int64_t>
- 必须确保该特化在任何调用
as<int64_t>()</int64_t>之前完成编译(不能只在头文件声明,要定义) - 不要在头文件里重复定义,否则链接时报
multiple definition -
std::stoll(..., 0)自动识别0x、0前缀,比硬写 base=10 更健壮
读取时直接用 as(),别依赖 operator>>
operator>>(即 node >> value)底层仍走 decode,但容易被误认为“更底层”,其实和 as() 等价;而 as() 语义更明确、调试时更容易定位问题。
- 安全读取示例:
YAML::Node config = YAML::LoadFile("config.yaml");
int64_t timeout_ms = config["timeout_ms"].as<int64_t>(); // ✅ 触发你注册的 decode</int64_t>
- 如果字段可能缺失或类型错误,加判断:
if (config["timeout_ms"] && config["timeout_ms"].IsScalar()) {
try {
int64_t val = config["timeout_ms"].as<int64_t>();
// use val
} catch (const YAML::BadConversion& e) {
// 处理解析失败,比如日志 + 默认值
}
}</int64_t>
注意 YAML 文件本身的数字书写格式
即使你注册了特化,YAML 解析器在词法分析阶段就决定了 token 类型。如果数字带小数点(如 1000.0)或指数(如 1e6),yaml-cpp 会直接标记为 float token,你的 decode 函数根本不会被调用——因为 node.IsScalar() 成立,但 node.Type() 是 YAML::NodeType::Scalar,内容却是浮点字面量。
- 确保 YAML 中长整型写成纯整数形式:
9223372036854775807,而非9223372036854775807.0或0x7FFFFFFFFFFFFFFF(后者虽能被stoll解析,但不是标准 YAML 整数格式,部分编辑器/校验工具会报错) - 十六进制支持靠
stoll实现,不是 yaml-cpp 原生能力,所以0xFF可行,但0b1010不行(stoll不支持二进制) - 超大整数字符串(如超过 20 位)建议用引号包裹成字符串再手动解析,避免 yaml-cpp 早期版本误判为 float
YAML::convert<int64_t></int64_t> 是必要步骤,漏掉它,所有“读取 int64_t”都是假象——背后全是 double 中转和潜在精度腐蚀。真正麻烦的不是写特化,而是忘记它只在定义它的编译单元生效,跨文件调用时静默失效。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











