...one of the most highly regarded and expertly designed C++ library projects in the world。
— Herb Sutter and Andrei Alexandrescu, C++ Coding Standards
功能
已弃用
ratio_string<>::short_name 和 ratio_string<>::long_name 已弃用。请分别使用 ratio_string<>::symbol 和 ratio_string<>::prefix。这些函数将在 1.55 中移除。
修复
功能
修复
Test
当前的 N3000 不允许复制构造或赋值具有相同归一化形式的比率类的 ratio 实例。
这个简单的例子
ratio
<1,3> r1;ratio
<3,9> r2; r1 = r2; // (1)
在 (1) 中无法编译。其他例子
ratio
<1,3> r1;ratio_subtract
<ratio
<2,3>,ratio
<1,3> > r2=r1; // (2)
ratio_subtract<ratio<2,3>,ratio<1,3>> 的类型可能是 ratio<3,9>,因此编译可能在 (2) 中失败。它也可能是 ratio<1,3>,并且编译成功。
LWG 1281 号问题的当前解决方案承认了对嵌套类型定义的需要,因此 Boost.Ratio 正在跟踪 std::ratio 的最终可能版本。
当结果可表示,但简单的算术规则应用会导致溢出时,例如 ratio_multiply<ratio<INTMAX_MAX,2>,ratio<2,INTMAX_MAX>> 可以简化为 ratio<1,1>,但 ratio<INTMAX_MAX*2,INTMAX_MAX*2> 的直接结果会导致溢出。
Boost.Ratio 实现了一些简化,以降低溢出的可能性。总体的思路是
以下子章节将详细介绍每种情况。
ratio_add
在
(n1/d1)+(n2/d2)=(n1*d2+n2*d1)/(d1*d2)
n1*d2+n2*d1 或 d1*d2 都可能溢出。
( (n1 * d2) + (n2 * d1) ) -------------------------- (d1 * d2)
通过 gcd(d1,d2) 分别对分子和分母进行除法
( (n1 * (d2/gcd(d1,d2))) + (n2 * (d1/gcd(d1,d2))) ) ---------------------------------------------------- ((d1 * d2) / gcd(d1,d2))
在分子中乘以和除以 gcd(n1,n2)
( ((gcd(n1,n2)*(n1/gcd(n1,n2))) * (d2/gcd(d1,d2))) + ((gcd(n1,n2)*(n2/gcd(n1,n2))) * (d1/gcd(d1,d2))) ) -------------------------------------------------- ( (d1 * d2) / gcd(d1,d2) )
因子化 gcd(n1,n2)
( gcd(n1,n2) * ( ((n1/gcd(n1,n2)) * (d2/gcd(d1,d2))) + ((n2/gcd(n1,n2)) * (d1/gcd(d1,d2))) ) ) ------------------------------------------------------------------------------- ( (d1 * d2) / gcd(d1,d2) )
重新组合
( gcd(n1,n2) * ( ((n1/gcd(n1,n2)) * (d2/gcd(d1,d2))) + ((n2/gcd(n1,n2)) * (d1/gcd(d1,d2))) ) ) ------------------------------------------------------------------------------- ( (d1 / gcd(d1,d2)) * d2 )
除以 (d1 / gcd(d1,d2))
( ( gcd(n1,n2) / (d1 / gcd(d1,d2)) ) * ( ((n1/gcd(n1,n2)) * (d2/gcd(d1,d2))) + ((n2/gcd(n1,n2)) * (d1/gcd(d1,d2))) ) ) ------------------------------------------------------------------------------- d2
除以 d2
( gcd(n1,n2) / (d1 / gcd(d1,d2)) ) * ( ((n1/gcd(n1,n2)) * (d2/gcd(d1,d2))) + ((n2/gcd(n1,n2)) * (d1/gcd(d1,d2))) / d2 )
这个表达式对应于两个风险较低的比率相乘,因为初始分子和分母现在大多数情况下都除以了一个 gcd。
对于 ratio_subtract,推理是相同的。
ratio_multiply
在
(n1/d1)*(n2/d2)=((n1*n2)/(d1*d2))
n1*n2 或 d1*d2 都可能溢出。
分子和分母除以 gcc(n1,d2)
(((n1/gcc(n1,d2))*n2) --------------------- (d1*(d2/gcc(n1,d2))))
除以 gcc(n2,d1)
((n1/gcc(n1,d2))*(n2/gcc(n2,d1))) --------------------------------- ((d1/gcc(n2,d1))*(d2/gcc(n1,d2)))
现在所有的初始分子和分母都已简化,避免了溢出。
对于 ratio_divide,推理是相似的。
ratio_less
为了计算
(n1/d1)<(n2/d2)
在不转换为浮点数的情况下,使用了两种技术
如果 sign(n1) < sign(n2),则结果为真。
如果 sign(n1) == sign(n2),则结果取决于以下情况,在将分子设为正数之后
我们称 Qi 为 ni 除以 di 的整数除法,Mi 为 ni 和 di 的模。
ni = Qi * di + Mi and Mi < di
形式
((n1*d2)<(d1*n2))
我们得到
(((Q1 * d1 + M1)*d2)<(d1*((Q2 * d2 + M2))))
展开
((Q1 * d1 * d2)+ (M1*d2))<((d1 * Q2 * d2) + (d1*M2))
除以 d1*d2
Q1 + (M1/d1) < Q2 + (M2/d2)
如果 Q1=Q2,结果取决于
(M1/d1) < (M2/d2)
如果 M1==0==M2,结果为假
如果 M1=0 M2!=0,结果为真
如果 M1!=0 M2==0,结果为假
如果 M1!=0 M2!=0,结果取决于
(d2/M2) < (d1/M1)
如果 Q1!=Q2,则结果
Q1 + (M1/d1) < Q2 + (M2/d2)
仅取决于 Q1 和 Q2,因为 Qi 是整数且 (Mi/di) <1,因为 Mi 如果 Q1>Q2,Q1==Q2+k,k>=1 但 0 和 1 之间的两个数字的差不能大于 1,所以结果为假。 如果 Q2>Q1,Q2==Q1+k,k>=1 这总是为真,所以结果为真。 下表总结了此分析 ratio<n1,d1> ratio<n2,d2> Q1 Q2 M1 M2 结果 ratio<n1,d1> ratio<n2,d2> Q1 Q2
!=0
!=0
Q1 < Q2 ratio<n1,d1> ratio<n2,d2> Q Q
0
0
false ratio<n1,d1> ratio<n2,d2> Q Q
0
!=0
true ratio<n1,d1> ratio<n2,d2> Q Q
!=0
0
false ratio<n1,d1> ratio<n2,d2> Q Q
!=0
!=0
ratio_less<ratio<d2,M2>, ratio<d1/M1>>Q2+k + (M1/d1) < Q2 + (M2/d2)
k + (M1/d1) < (M2/d2)
k < (M2/d2) - (M1/d1)
Q1 + (M1/d1) < Q1+k + (M2/d2)
(M1/d1) < k + (M2/d2)
(M1/d1) - (M2/d2) < k
库代码源自 Howard Hinnant 的 time2_demo
原型。非常感谢 Howard 将他的代码在 Boost 许可下提供。原始代码由 Beman Dawes 修改,以符合 Boost 约定。
time2_demo
包含以下注释
非常感谢 Andrei Alexandrescu、Walter Brown、Peter Dimov、Jeff Garland、Terry Golubiewski、Daniel Krugler、Anthony Williams。
Howard Hinnant,作为该库的真正作者,在库的开发过程中提供了宝贵的反馈和建议。特别是,ratio_io.hpp 源代码改编自 Howard Hinnant 的实验性头文件 <ratio_io>
。
Boost.Ratio 的接受审查于 2010 年 10 月 2 日至 11 日之间进行。非常感谢审查经理 Anthony Williams 以及所有审查员:Bruno Santos、Joel Falcou、Robert Stewart、Roland Bock、Tom Tan 和 Paul A. Bristol。
感谢 Andrew Chinoff 和 Paul A. Bristol 帮助完善文档。
要进行测试,您需要运行
bjam libs/ratio/test
您也可以通过以下方式运行特定的测试套件
cd libs/chrono/test bjam ratio
名称 |
类型 |
描述 |
结果 |
工单 |
---|---|---|---|---|
typedefs.pass |
运行 |
检查预定义 typedefs 的 num/den 是否正确 |
通过 |
# |
ratio.pass |
运行 |
检查 num/den 是否已正确简化 |
通过 |
# |
ratio1.fail |
编译失败 |
模板参数 D 不能为零 |
通过 |
# |
ratio2.fail |
编译失败 |
模板参数 N 和 D 的绝对值必须可以用 intmax_t 类型表示 |
通过 |
# |
ratio3.fail |
编译失败 |
模板参数 N 和 D 的绝对值必须可以用 intmax_t 类型表示 |
通过 |
# |
名称 |
类型 |
描述 |
结果 |
工单 |
---|---|---|---|---|
ratio_equal.pass |
运行 |
检查 ratio_equal 元函数类 |
通过 |
# |
ratio_not_equal.pass |
运行 |
检查 ratio_not_equal 元函数类 |
通过 |
# |
ratio_less.pass |
运行 |
检查 ratio_less 元函数类 |
通过 |
# |
ratio_less_equal.pass |
运行 |
检查 ratio_less_equal 元函数类 |
通过 |
# |
ratio_greater.pass |
运行 |
检查 ratio_greater 元函数类 |
通过 |
# |
ratio_greater_equal.pass |
运行 |
检查 ratio_greater_equal 元函数类 |
通过 |
# |
名称 |
类型 |
描述 |
结果 |
工单 |
---|---|---|---|---|
ratio_add.pass |
运行 |
检查 ratio_add 元函数类 |
通过 |
# |
ratio_subtract.pass |
运行 |
检查 ratio_subtract 元函数类 |
通过 |
# |
ratio_multiply.pass |
运行 |
检查 ratio_multiply 元函数类 |
通过 |
# |
ratio_divide.pass |
运行 |
检查 ratio_divide 元函数类 |
通过 |
# |
ratio_add.fail |
编译失败 |
检查 ratio_add 溢出元函数类 |
通过 |
# |
ratio_subtract.fail |
编译失败 |
检查 ratio_subtract 下溢元函数类 |
通过 |
# |
ratio_multiply.fail |
编译失败 |
检查 ratio_multiply 溢出元函数类 |
通过 |
# |
ratio_divide.fail |
编译失败 |
检查 ratio_divide 溢出元函数类 |
通过 |
# |
工单 |
描述 |
解决方案 |
State |
---|---|---|---|
1 |
ratio_multiply 和 ratio_divide 元函数的结果不是归一化的比率。 |
在 ratio 算术运算中使用嵌套的 ratio 类型定义。 |
已关闭 |
2 |
INTMAX_C 并非总是已定义。 |
将 INTMAX_C 替换为 BOOST_INTMAX_C,直到 boost/cstdint.hpp 确保 INTMAX_C 始终已定义。 |
已关闭 |
3 |
MSVC 在出现整数常量溢出时报告警告而不是错误。 |
处理 MSVC 在出现整数常量溢出时报告警告而不是错误的情况。 |
已关闭 |
4 |
ratio_less 在可以避免的情况下发生溢出。 |
更改 libc++ 中实现的算法。 |
已关闭 |