Boost C++ 库

...one of the most highly regarded and expertly designed C++ library projects in the world。 Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

附录 - Boost C++ 函数库
PrevUpHomeNext

新功能

  • #XXXX 添加 ratio_power。
  • #XXXX 添加 IEC 二进制前缀。

修复

  • #7616 br_mul::nan - 警告 C4293:“<<”:移位计数为负或过大,未定义行为。

功能

  • 将 short_name 和 long_name 函数分别替换为 symbol 和 prefix 函数。

已弃用

ratio_string<>::short_name 和 ratio_string<>::long_name 已弃用。请分别使用 ratio_string<>::symbol 和 ratio_string<>::prefix。这些函数将在 1.55 中移除。

修复

  • #7478 支持 char16_t 和 char32_t 的编译器在不提供 std::u16string 和 std::u32string 时编译失败。

修复

  • #7075 解决错误:模板参数常量“n1”的类型取决于另一个模板参数。

修复

  • #6498 boost::ratio 在默认设置下无法编译。

功能

  • 添加了 MPL 有理数常量以及相关的数值元函数特化。
  • 将 ratio 移至主干。
  • 文档修订。

修复

  • 因许可问题不兼容,移除了 LLVM 适配文件。

功能

  • 添加了 ratio_string 特性。

修复

  • ratio_less 避免了溢出,遵循了 libc++ 的算法。

Test

  • 包含了一个更完整的测试,改编自 libc++/ratio 的测试。

功能

  • Ratio 已从 Boost.Chrono 中提取。
为什么 ratio 需要从具有相同归一化形式的比率进行复制构造和赋值

当前的 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>,并且编译成功。

为什么 ratio 需要嵌套的 normalizer 类型定义

LWG 1281 号问题的当前解决方案承认了对嵌套类型定义的需要,因此 Boost.Ratio 正在跟踪 std::ratio 的最终可能版本。

Boost.Ratio 如何尝试避免编译时有理数算术溢出?

当结果可表示,但简单的算术规则应用会导致溢出时,例如 ratio_multiply<ratio<INTMAX_MAX,2>,ratio<2,INTMAX_MAX>> 可以简化为 ratio<1,1>,但 ratio<INTMAX_MAX*2,INTMAX_MAX*2> 的直接结果会导致溢出。

Boost.Ratio 实现了一些简化,以降低溢出的可能性。总体的思路是

  • ratio<> 的 num 和 den 字段已归一化。
  • 使用一些可能溢出的乘积的 gcd,并在乘积之前进行简化。
  • 使用一些避免加法或减法(可能导致溢出或下溢)的等价关系。

以下子章节将详细介绍每种情况。

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

Q2+k + (M1/d1) < Q2 + (M2/d2)
k + (M1/d1) < (M2/d2)
k < (M2/d2) - (M1/d1)

但 0 和 1 之间的两个数字的差不能大于 1,所以结果为假。

如果 Q2>Q1,Q2==Q1+k,k>=1

Q1 + (M1/d1) < Q1+k + (M2/d2)
(M1/d1) < k + (M2/d2)
(M1/d1) - (M2/d2) < k

这总是为真,所以结果为真。

下表总结了此分析

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>>

库代码源自 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++ 中实现的算法。

已关闭

供后续版本使用
  • 在提供模板别名的编译器上使用模板别名。
  • 实现 多参数 ratio 算术运算。

PrevUpHomeNext