JSON for Modern C++ 数值类型探秘:深入理解 number_integer_t 有符号整数类型

发布时间:2026/9/8 18:30:56
JSON for Modern C++ 数值类型探秘:深入理解 number_integer_t 有符号整数类型 JSON for Modern C 数值类型探秘深入理解 number_integer_t 有符号整数类型【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json# JSON for Modern C 数值类型探秘深入理解 number_integer_t 有符号整数类型number_integer_t是 nlohmann/jsonJSON for Modern C中用于存储JSON 有符号整数的核心类型别名。本文基于 number_integer_t 官方 API 文档 展开结合仓库头文件源码与词法分析器实现讲解它的定义方式、默认类型、边界限制、与number_unsigned_t/number_float_t的分工关系以及整数在反序列化过程中的存储与溢出回退机制。读完本文你将能够在自己的工程中准确判断整数值的容量边界、正确解释超大整数自动降级为浮点等行为并掌握如何通过模板参数自定义该类型。一、从 RFC 8259 说起JSON 为什么需要三种数值类型RFC 8259 对 JSON 数值number给出了定义式的描述数值的表示与大多数编程语言类似使用十进制数字、以 10 为基数。一个数包含一个整数部分可选地以负号开头其后可以跟小数部分和/或指数部分。前导零是不允许的。……无法用下述文法表示的数值如Infinity和NaN是不允许的。这条定义实际上同时涵盖了整数与浮点数两类数值。但 C 恰好提供了比一个笼统的 number更精确的存储能力——只要明确一个数到底是有符号整数、无符号整数还是浮点数就能做到既不浪费精度、也尽可能接近机器原生表示。因此JSON for Modern C 没有沿用单一数值类型而是把 JSON 数值拆成了三个相互独立、由basic_json模板参数驱动的类型别名类型别名模板参数存储的 JSON 数值文档number_integer_tNumberIntegerType有符号整数本文number_unsigned_tNumberUnsignedType无符号整数number_unsigned_tnumber_float_tNumberFloatType浮点数number_float_t在源码层面这三个别名在basic_json类的JSON value data types段落中集中声明见 include/nlohmann/json.hpp/// brief a type for a number (integer) using number_integer_t NumberIntegerType; // line 386 /// brief a type for a number (unsigned) using number_unsigned_t NumberUnsignedType; // line 390 /// brief a type for a number (floating-point) using number_float_t NumberFloatType; // line 394也就是说number_integer_t并不是一个独立设计的类而是模板参数NumberIntegerType的直接别名using number_integer_t NumberIntegerType;二、默认类型与模板参数的来源basic_json类模板在 include/nlohmann/json_fwd.hpp 中完成前置声明全部数值相关模板参数的默认值都收敛于此template templatetypename U, typename V, typename... Args class ObjectType std::map, templatetypename U, typename... Args class ArrayType std::vector, class StringType std::string, class BooleanType bool, class NumberIntegerType std::int64_t, // 有符号整数默认 std::int64_t class NumberUnsignedType std::uint64_t, // 无符号整数默认 std::uint64_t class NumberFloatType double, // 浮点数默认 double ... class basic_json;因此在使用默认模板参数即直接使用nlohmann::json这个特化别名时number_integer_t的默认值就是#!cpp std::int64_t。这也解释了为什么本文标题用到的官方示例能够以编译期方式验证这一点。仓库中的完整示例见 examples/number_integer_t.cpp#include iostream #include iomanip #include nlohmann/json.hpp using json nlohmann::json; int main() { std::cout std::boolalpha std::is_samestd::int64_t, json::number_integer_t::value std::endl; }其运行输出number_integer_t.output为truestd::is_samestd::int64_t, json::number_integer_t::value在编译期判定json::number_integer_t与std::int64_t是否为同一类型。结果为true即以默认配置编译时整数存储单元就是 64 位有符号整数。三、默认行为前导零与八进制的语言陷阱原文档特别提醒了一个极易踩坑的差异C 并不强制 JSON 文法中禁止前导零的约束。JSON 字符串反序列化对前导零是严格报错的但如果你在 C 源代码里直接写出带前导零的整数字面量C 编译器会按八进制解释它。举例来说C 整数字面量010在语法层面等于十进制的 8因此json j 010; // C 层八进制 010 十进制 8 std::cout j.dump() std::endl; // 输出8也就是说内部保存的十进制值8在序列化dump时得到的是8而不是010。反方向再看反序列化如果在 JSON 文本中写入带前导零的内容例如{a: 010}解析阶段会直接抛出parse_error这正是词法分析器严格遵循 RFC 8259 文法的结果。从源码看词法分析器 include/nlohmann/detail/input/lexer.hpp 中的scan_number()用一套显式状态机scan_number_minus、scan_number_zero、scan_number_any1、scan_number_decimal1、scan_number_exponent等goto标签逐字符吞入数字令牌。scan_number_zero状态意味着当前只读到了一个0此时若紧接着读到的是数字字符便会被判定为非法前导零而进入错误分支——这正是文档所述反序列化时前导零产生错误的实现根基。四、整数容量边界INT64_MIN 与 INT64_MAXRFC 8259 允许实现方对数值的范围和精度施加限制An implementation may set limits on the range and precision of numbers.默认使用std::int64_t时JSON for Modern C 的有符号整数存储能力为项目值可存储的最大整数9223372036854775807即INT64_MAX可存储的最小整数-9223372036854775808即INT64_MIN两个边界上的注意事项构造阶段通过构造函数放入超出该区间的整数例如直接用一个 128 位或字面量超界的值构造json会触发有符号整数的溢出/下溢over/underflow行为。这一点沿用的是 C 整型本身的语义使用时应当自行保证取值在[INT64_MIN, INT64_MAX]内。反序列化阶段如果输入 JSON 中的整数字面量超出std::int64_t的表示范围库不会报错丢弃而是采用就近降级策略自动存储为其他两种类型数值过大超过INT64_MAX但可用uint64_t表示→ 自动存储为number_unsigned_t数值进一步超界或形态上更适合 → 自动存储为number_float_t。上述机制在词法分析器 lexer.hpp 的scan_number_done阶段有清晰的代码证据库先尝试解析整数、解析失败再回退到浮点// try to parse integers first and fall back to floats if (number_type token_type::value_unsigned) { const auto x std::strtoull(token_buffer.data(), endptr, 10); ... if (errno ! ERANGE) { value_unsigned static_castnumber_unsigned_t(x); if (value_unsigned x) { return token_type::value_unsigned; } } } else if (number_type token_type::value_integer) { const auto x std::strtoll(token_buffer.data(), endptr, 10); ... if (errno ! ERANGE) { value_integer static_castnumber_integer_t(x); if (value_integer x) { return token_type::value_integer; } } } // this code is reached if we parse a floating-point number or if an // integer conversion above failed strtof(value_float, token_buffer.data(), endptr); ... return token_type::value_float;可以看到无符号数尝试std::strtoull、有符号数尝试std::strtoll一旦errno ERANGE溢出或static_cast回绕后与原始值不等代码就会落到末尾把令牌交给strtof解析并以value_float令牌收尾——数值由此被归入浮点阵营。整套逻辑与官方文档反序列化时太大或太小的整数会自动被存储为number_unsigned_t或number_float_t的描述完全吻合。4.1 三种令牌与运行时类型词法分析器用一组枚举令牌区分解析结果见 lexer.hppvalue_unsigned, /// an unsigned integer -- use get_number_unsigned() for actual value value_integer, /// a signed integer -- use get_number_integer() for actual value value_float, /// a floating point number -- use get_number_float() for actual value分别对应解析器parser随后调用get_number_unsigned()、get_number_integer()、get_number_float()取值再落到value_t::number_unsigned、value_t::number_integer、value_t::number_float三个运行时类型标签之一。五、互操作区间[-2^531, 2^53-1]RFC 8259 还给出了关于跨实现一致性的指导性说明Note that when such software is used, numbers that are integers and are in the range $[-2^{53}1, 2^{53}-1]$ are interoperable in the sense that implementations will agree exactly on their numeric values.[-2^531, 2^53-1]这个区间之所以存在是因为大量 JSON 实现尤其基于 JavaScript / IEEE 754 双精度浮点的实现只能精确表示该范围内的整数。而 JSON for Modern C 的默认有符号整数类型std::int64_t的精确表示区间是[INT64_MIN, INT64_MAX][-2^531, 2^53-1] ⊂ [INT64_MIN, INT64_MAX]互操作区间是自身精确支持区间的真子集因此可以得出两个结论凡是落在[-2^531, 2^53-1]内的整数本库都能与其他遵守该约定的实现逐位精确一致地解析与序列化不会出现精度丢失超出该子集、但仍在std::int64_t范围内的整数本库仍然能无损保存只是在与基于双精度浮点的其他系统交换时对方可能无法精确表示——这一点是设计上的安全余量而非缺陷。六、存储方式直接内嵌的联合体成员JSON for Modern C 在值存储上采取类型标签 联合体的紧凑布局。整数以及其他数值、布尔、字符串指针被直接存放在一个匿名union中见 include/nlohmann/json.hppunion json_value { number_integer_t number_integer; // 有符号整数成员即 number_integer_t number_unsigned_t number_unsigned; number_float_t number_float; boolean_t boolean; string_t* string; object_t* object; array_t* array; ... json_value(number_integer_t v) noexcept : number_integer(v) {} ... };结合本节之前的讨论可以总结出直接存储的两层含义布局上number_integer_t类型的值就是json_value联合体中的一个原生成员一个json对象对整数值不存在二级堆分配或间接指针读写都落在联合体自身的内存上语义上运行类型由m_data.m_type取自value_t枚举与联合体成员共同决定。例如is_number_integer()的判定实现include/nlohmann/json.hpp同时涵盖了有符号与无符号两种标签constexpr bool is_number_integer() const noexcept { return m_data.m_type value_t::number_integer || m_data.m_type value_t::number_unsigned; }这提示读者在JSON for Modern C中is_number_integer()的语义更接近这是一个整型数值不分正负而无符号整数值同样会被视为整型若要严格区分正负需要结合is_number_unsigned()一起判断。七、自定义 NumberIntegerType 与使用建议既然number_integer_t完全由basic_json的模板参数NumberIntegerType决定那么当项目对整数范围有特殊要求时可以通过特化basic_json替换它。例如#include nlohmann/json.hpp #include cstdint // 使用 __int128 等扩展类型前务必确认目标编译器的支持情况 // 下面以常见的“改用 32 位有符号整数”为例示意特化方法 template templatetypename U, typename V, typename... Args class ObjectType std::map, templatetypename U, typename... Args class ArrayType std::vector, class StringType std::string, class BooleanType bool, class NumberIntegerType std::int32_t, // 关键替换有符号整数类型 class NumberUnsignedType std::uint32_t, class NumberFloatType double, templatetypename U class AllocatorType std::allocator using my_json nlohmann::basic_json ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType; static_assert(std::is_samemy_json::number_integer_t, std::int32_t::value, custom integer type applied);需要特别强调的是改动NumberIntegerType时应同时留意配套的NumberUnsignedType确保两者表示能力匹配参考前文默认的int64_t/uint64_t对称设计number_integer_t的类型变化会影响整库的算术、比较与序列化路径属于全局性 ABI 级调整一般仅在确有跨端整型协议对齐需求时才建议特化多数场景下直接使用默认的std::int64_t并遵循前文的范围约束即可满足要求。八、版本与适用范围number_integer_t自 JSON for Modern C1.0.0版本起便已提供属于该库最基础的数值承载类型之一其语义在历代版本中保持稳定。本文所有源码证据均来自仓库当前 3.12.0 版本读者若使用不同大版本建议以对应版本的 single_include/nlohmann/json.hpp单头文件形态或 include/nlohmann/json.hpp分模块形态中的实际声明为准。小结number_integer_t用一句using number_integer_t NumberIntegerType;概括了 JSON for Modern C 对有符号整数的全部设计哲学以模板参数注入类型、以std::int64_t为默认、以整数优先 浮点回退的词法策略兜底超界输入并以联合体直接内嵌的方式零开销存储。理解它的默认值与边界行为是准确预估json对象内存布局、排查大整数变浮点类精度问题的第一步也是进一步阅读number_unsigned_t、number_float_t两篇姊妹文档的基础。【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考