double解析大整数会丢精度,因JSON数字按IEEE 754双精度表示,超过2^53-1(约9e15)时无法精确表示每个整数,导致如9007199254740993被误读为9007199254740992。

为什么double解析大整数会丢精度?
JSON标准里没有整数类型,所有数字都按IEEE 754双精度浮点表示。当整数超过2^53 - 1(约9e15)时,double无法精确表示每个整数——比如9007199254740993会被读成9007199254740992。
常见错误现象:后端返回的订单ID、时间戳(毫秒级)、区块链地址余额等大整数字段,在C++里用double或int64_t直接强转后值不对。
- 别依赖
json::get<int64_t>()</int64_t>——多数库(如nlohmann/json)默认走double路径再截断 - 别用
std::stoll()直接转换std::string——若原始JSON里是数字而非字符串,解析阶段已失真 - 确认你的JSON库是否支持“保真解析”:即把大数字原样作为字符串保留,而非急着转成数值
nlohmann/json如何启用number_integer和number_unsigned保真解析
nlohmann/json 3.10+ 默认把所有JSON数字统一当作double处理,但可通过自定义json_sax接口拦截原始token,或改用parse的strict模式配合number_integer回调。
更实用的做法是:在解析前告诉库“遇到大整数别转double,先存字符串”,再按需转boost::multiprecision::cpp_int或__int128(GCC):
立即学习“C++免费学习笔记(深入)”;
nlohmann::json j = nlohmann::json::parse(R"({"id": 9007199254740992123})", nullptr, false, true);
// 第4个参数enable_exceptions=true,第3个参数allow_exceptions=false可关异常,但关键在:
// 必须配合自定义parser,或改用raw_json_t + 手动词法分析
实际推荐路径:
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
- 升级到nlohmann/json ≥ 3.11.2,启用
NLOHMANN_JSON_DISABLE_LEGACY_FLOAT_TYPES宏防止隐式double降级 - 对关键字段,显式用
j["id"].dump()拿到原始JSON片段,再用boost::lexical_cast<__int128></__int128>或mpz_import(GMP)解析 - 若字段固定为超大整数,预定义结构体时直接声明为
std::string,避免任何数值转换
用simdjson直接跳过浮点解析,拿到原始数字文本
simdjson的DOM API默认仍走double,但它提供raw_json_string视图,能零拷贝获取数字字面量原始字符——这是避免精度丢失最可靠的方式。
示例:提取{"amount": 12345678901234567890123}中的精确值:
simdjson::ondemand::parser parser;
auto doc = parser.parse(R"({"amount": 12345678901234567890123})"_padded);
simdjson::ondemand::object obj = doc.get_object();
auto amount_token = obj["amount"]; // 不调用get_double()或get_int64()
std::string_view sv = amount_token.raw_json_token(); // 得到"12345678901234567890123"
// 后续用absl::SimpleAtoi、cpp_int、或手写字符串转大整数
-
raw_json_token()返回的是不含空格、括号的纯数字字符串,不带引号 - 注意:该API仅在
ondemand模式下可用,dom模式不支持 - 如果JSON来自不可信来源,需验证
sv是否全为ASCII数字(排除科学计数法如"1e100")
什么时候必须用任意精度整数库?
当业务要求严格等于、参与加密运算、或与Java/Python的BigInteger交互时,int128_t也不够用(最大约1e38),得上任意精度。
轻量级选型建议:
- 简单场景:用
boost::multiprecision::int128_t(编译期确定大小)或cpp_int(动态分配) - 高性能/嵌入式:用
libtommath(单头文件,无依赖) - 已有GMP:直接用
mpz_class,但注意ABI兼容性(不同版本GMP可能二进制不兼容)
关键提醒:无论用哪个库,都必须从JSON原始字符串开始解析——任何中间经过double或long long的环节,精度就永久丢了。

















