std::ratio是编译期有理数模板,仅支持整数比值的静态表示与四则运算;所有计算通过ratio_add等元函数在编译期完成并自动约分,结果仍为std::ratio类型,不支持运行时计算、浮点转换或溢出保护。

ratio 是什么,它真能做编译期有理数计算?
能,但仅限于整数比值的静态表示和四则运算(加减乘除),所有计算都在编译期完成,不生成任何运行时代码。它不是通用“有理数类型”,而是 std::ratio 提供的模板元编程工具,底层靠 num 和 den 两个整型常量表达最简分数。
常见误用是试图用 ratio 存储浮点结果或参与变量运算——它只接受字面整数模板参数,比如 std::ratio 合法,std::ratio<a b></a>(a、b 是变量)直接编译失败。
怎么进行加减乘除?必须用 ratio_add 等辅助模板
std::ratio 本身不重载 + 或 * 运算符;所有算术操作都通过配套的元函数完成,结果仍是 std::ratio 类型,且自动约分到最简形式。
-
std::ratio_add<r1 r2></r1>:返回R1 + R2的最简ratio,例如ratio_add<ratio>, ratio>::type</ratio>得到ratio -
std::ratio_subtract<r1 r2></r1>、std::ratio_multiply<r1 r2></r1>、std::ratio_divide<r1 r2></r1>同理 - 所有输入必须是合法
ratio(den != 0,且不能溢出intmax_t);否则编译报错,典型错误信息:static assertion failed: ratio overflow
示例:
立即学习“C++免费学习笔记(深入)”;
using r1 = std::ratio<1, 2>; using r2 = std::ratio<1, 3>; using sum = std::ratio_add<r1, r2>::type; // ratio<5, 6> static_assert(sum::num == 5 && sum::den == 6, "");
为什么有时编译失败?注意溢出、零分母和负号处理
三个高频翻车点:
-
den为 0:如std::ratio,编译器直接拒绝,错误提示含division by zero - 中间计算溢出:比如
ratio_multiply<ratio>, ratio></ratio>,分子 10¹² 可能超出intmax_t(通常为 int64_t),触发ratio overflow - 负号归一化:
std::ratio自动转为ratio,std::ratio转为ratio——符号只保留在分子,分母恒正。依赖分母带负号的逻辑会失效
和 constexpr double 混用时要注意什么?
ratio 的值只能通过 ::num 和 ::den 拿到整数,想转成浮点数必须显式转换:
constexpr double v = static_cast<double>(std::ratio<1, 3>::num) / std::ratio<1, 3>::den; // 0.333... // 错误写法:double v = std::ratio<1,3>; // no conversion operator
这个转换发生在编译期,但一旦赋给 double 就失去精确性;如果后续还需精确分数运算,别提前转浮点——保留 ratio 类型链更安全。
真正容易被忽略的是:ratio 运算不支持约分控制(比如强制保留分母为 100),也不支持开方、取模等非有理运算。它只是编译期分数代数的一小块拼图,够用,但别指望它替代 boost::rational 或自定义有理数类。


















