![]() |
Home | Libraries | People | FAQ | More |
By using MPL metafunctions and the template specializations for operations
on composite dimensions (defined in boost/units/dimension.hpp)
it is possible to perform compile time arithmetic according to the dimensional
analysis rules described above
to produce new composite dimensions :
在復合量綱(composite dimension)(定義於boost/units/dimension.hpp文件)基礎上通過MPL元函數(metafunction)和模板特化(template specialization)按照之前介紹的量綱分析法則(dimensional analysis rules)執行編譯時運算(perform compile time arithmetic)得到新的復合量綱(composite dimension):
typedef mpl::times<length_dimension,mass_dimension>::type LM_type; typedef mpl::divides<length_dimension,time_dimension>::type L_T_type; typedef static_root< mpl::divides<energy_dimension,mass_dimension>::type, static_rational<2> >::type V_type;
outputting (with symbol demangling, implemented in boost/units/detail/utility.hpp)
通過在boost/units/detail/utility.hpp中實現的符號重整(symbol demangling)後輸出(下面的數字11、21應該是1、2,原文應該錯了,譯者注)
length_dimension = list<dim<length_base_dimension, static_rational<1l, 1l> >, dimensionless_type> mass_dimension = list<dim<mass_base_dimension, static_rational<1l, 1l> >, dimensionless_type> time_dimension = list<dim<time_base_dimension, static_rational<1l, 1l> >, dimensionless_type> energy_dimension = list<dim<length_base_dimension, static_rational<2l, 1l> >, list<dim<mass_base_dimension, static_rational<1l, 1l> >, list<dim<time_base_dimension, static_rational<-2l, 1l> >, dimensionless_type> > > LM_type = list<dim<length_base_dimension, static_rational<1l, 1l> >, list<dim<mass_base_dimension, static_rational<1l, 1l> >, dimensionless_type> > L_T_type = list<dim<length_base_dimension, static_rational<1l, 1l> >, list<dim<time_base_dimension, static_rational<-1l, 1l> >, dimensionless_type> > V_type = list<dim<length_base_dimension, static_rational<1l, 1l> >, list<dim<time_base_dimension, static_rational<-1l, 1l> >, dimensionless_type> >
(unit.cpp)
This example demonstrates the use of the simple but functional unit system
implemented in libs/units/example/test_system.hpp
:
這個例子展示了一個簡單但實用的單位系統,在libs/units/example/test_system.hpp文件中實現:
const length L; const mass M; // needs to be namespace-qualified because of global time definition const boost::units::test::time T; const energy E;
We can perform various algebraic operations on these units, resulting in
the following output:
我們可以在這些單位上執行很多代數運算,輸出如下:
L = m L+L = m L-L = m L/L = dimensionless meter*meter = m^2 M*(L/T)*(L/T) = m^2 kg s^-2 M*(L/T)^2 = m^2 kg s^-2 L^3 = m^3 L^(3/2) = m^(3/2) 2vM = kg^(1/2) (3/2)vM = kg^(2/3)
This example demonstrates how to use quantities of our toy unit system :
這個例子展示了如何在我們的單位系統(unit system)上使用數量(quantity):
quantity<length> L = 2.0*meters; // quantity of length quantity<energy> E = kilograms*pow<2>(L/seconds); // quantity of energy
giving us the basic quantity functionality :
會有最基本的數量(quantity)的功能:
L = 2 m L+L = 4 m L-L = 0 m L*L = 4 m^2 L/L = 1 dimensionless L*meter = 2 m^2 kilograms*(L/seconds)*(L/seconds) = 4 m^2 kg s^-2 kilograms*(L/seconds)^2 = 4 m^2 kg s^-2 L^3 = 8 m^3 L^(3/2) = 2.82843 m^(3/2) 2vL = 1.41421 m^(1/2) (3/2)vL = 1.5874 m^(2/3)
As a further demonstration of the flexibility of the system, we replace the
double value type with a std::complex<double> value type (ignoring the question of
the meaningfulness of complex lengths and energies) :
為了進一步展示系統的適應性(flexibility),我們把數值類型(value type)從double換成std::complex<double>(請無視長度和能量的複數含義):
quantity<length,std::complex<double> > L(std::complex<double>(3.0,4.0)*meters); quantity<energy,std::complex<double> > E(kilograms*pow<2>(L/seconds));
and find that the code functions exactly as expected with no additional work,
delegating operations to std::complex<double> and performing the appropriate dimensional
analysis :
這些代碼只是把操作符轉發給std::complex<double>類並執行了適當的量綱分析:
L = (3,4) m L+L = (6,8) m L-L = (0,0) m L*L = (-7,24) m^2 L/L = (1,0) dimensionless L*meter = (3,4) m^2 kilograms*(L/seconds)*(L/seconds) = (-7,24) m^2 kg s^-2 kilograms*(L/seconds)^2 = (-7,24) m^2 kg s^-2 L^3 = (-117,44) m^3 L^(3/2) = (2,11) m^(3/2) 2vL = (2,1) m^(1/2) (3/2)vL = (2.38285,1.69466) m^(2/3)
This example provides a fairly extensive set of tests covering most of the
quantity
functionality. It uses the SI unit system defined in boost/units/systems/si.hpp.
這個例子為quantity的功能提供了一個比較全面的測試,使用定義在boost/units/systems/si.hpp文件中的SI單位系統。
If we define a few units and associated quantities,
我們定義幾個單位(unit)和數量(quantity),
/// scalar const double s1 = 2; const long x1 = 2; const static_rational<4,3> x2; /// define some units force u1 = newton; energy u2 = joule; /// define some quantities quantity<force> q1(1.0*u1); quantity<energy> q2(2.0*u2);
the various algebraic operations between scalars, units, and quantities give
這幾個標量、單位和數量之間的代數運算結果是
S1 : 2 X1 : 2 X2 : (4/3) U1 : N U2 : J Q1 : 1 N Q2 : 2 J
Scalar/unit operations :
標量和單位之間的運算:
U1*S1 : 2 N S1*U1 : 2 N U1/S1 : 0.5 N S1/U1 : 2 m^-1 kg^-1 s^2
Unit/unit operations and integral/rational powers of units :
單位和單位之間的運算以及單位的整數/有理數乘方:
U1+U1 : N U1-U1 : N U1*U1 : m^2 kg^2 s^-4 U1/U1 : dimensionless U1*U2 : m^3 kg^2 s^-4 U1/U2 : m^-1 U1^X : m^2 kg^2 s^-4 X1vU1 : m^(1/2) kg^(1/2) s^-1 U1^X2 : m^(4/3) kg^(4/3) s^(-8/3) X2vU1 : m^(3/4) kg^(3/4) s^(-3/2)
Scalar/quantity operations :
標量和數量之間的運算:
Q1*S1 : 2 N S1*Q1 : 2 N Q1/S1 : 0.5 N S1/Q1 : 2 m^-1 kg^-1 s^2
Unit/quantity operations :
單位和數量之間的運算:
U1*Q1 : 1 m^2 kg^2 s^-4 Q1*U1 : 1 m^2 kg^2 s^-4 U1/Q1 : 1 dimensionless Q1/U1 : 1 dimensionless
Quantity/quantity operations and integral/rational powers of quantities :
數量和數量之間的運算以及數量的整數/有理數次乘方:
+Q1 : 1 N -Q1 : -1 N Q1+Q1 : 2 N Q1-Q1 : 0 N Q1*Q1 : 1 m^2 kg^2 s^-4 Q1/Q1 : 1 dimensionless Q1*Q2 : 2 m^3 kg^2 s^-4 Q1/Q2 : 0.5 m^-1 Q1^X1 : 1 m^2 kg^2 s^-4 X1vQ1 : 1 m^(1/2) kg^(1/2) s^-1 Q1^X2 : 1 m^(4/3) kg^(4/3) s^(-8/3) X2vQ1 : 1 m^(3/4) kg^(3/4) s^(-3/2)
Logical comparison operators are also defined between quantities :
數量之間同樣定義了邏輯比較運算:
/// check comparison tests quantity<length> l1(1.0*meter), l2(2.0*meters);
giving
l1 == l2 false l1 != l2 true l1 <= l2 true l1 < l2 true l1 >= l2 false l1 > l2 false
Implicit conversion is allowed between dimensionless quantities and their
corresponding value types :
無量綱數量和其數值類型之間可以隱式轉換:
/// check implicit unit conversion from dimensionless to value_type const double dimless = (q1/q1);
A generic function for computing mechanical work can be defined that takes
force and distance arguments in an arbitrary unit system and returns energy
in the same system:
下面這個計算機械功的泛型函數接受任意單位系統的力和距離作為參數並返回相同系統的功:
/// the physical definition of work - computed for an arbitrary unit system template<class System,class Y> quantity<unit<energy_dimension,System>,Y> work(quantity<unit<force_dimension,System>,Y> F, quantity<unit<length_dimension,System>,Y> dx) { return F*dx; }
/// test calcuation of work quantity<force> F(1.0*newton); quantity<length> dx(1.0*meter); quantity<energy> E(work(F,dx));
which functions as expected for SI quantities :
對於SI數量函數工作正確:
F = 1 N dx = 1 m E = 1 J
The ideal gas law can also be implemented in SI units :
理想氣體定律同樣可以用SI單位實現:
/// the ideal gas law in si units template<class Y> quantity<si::amount,Y> idealGasLaw(const quantity<si::pressure,Y>& P, const quantity<si::volume,Y>& V, const quantity<si::temperature,Y>& T) { using namespace boost::units::si; #if BOOST_UNITS_HAS_TYPEOF using namespace constants::codata; return (P*V/(R*T)); #else return P*V/(8.314472*(joules/(kelvin*mole))*T); #endif // BOOST_UNITS_HAS_TYPEOF }
/// test ideal gas law quantity<temperature> T = (273.+37.)*kelvin; quantity<pressure> P = 1.01325e5*pascals; quantity<length> r = 0.5e-6*meters; quantity<volume> V = (4.0/3.0)*3.141592*pow<3>(r); quantity<amount> n(idealGasLaw(P,V,T));
with the resulting output :
r = 5e-07 m P = 101325 Pa V = 5.23599e-19 m^3 T = 310 K n = 2.05835e-17 mol R = 8.314472 m^2 kg s^-2 K^-1 mol^-1 (rel. unc. = 1.8e-06)
Trigonometric and inverse trigonometric functions can be implemented for
any unit system that provides an angular base dimension. For radians, these
functions are found in boost/units/cmath.hpp
These behave as one expects, with trigonometric functions taking an angular
quantity and returning a dimensionless quantity, while the inverse trigonometric
functions take a dimensionless quantity and return an angular quantity :
三角函數和反三角函數也能在任意提供角度基礎量綱的單位系統上實現。
對於弧度,這些函數定義在boost/units/cmath.hpp。
這些函數就像你想的那樣,三角函數以弧度數量為參數返回一個無量綱數量,反三角函數以無量綱數量為參數返回一個弧度數量:
Defining a few angular quantities,
定義幾個弧度數量,
/// test trig stuff quantity<plane_angle> theta = 0.375*radians; quantity<dimensionless> sin_theta = sin(theta); quantity<plane_angle> thetap = asin(sin_theta);
yields
theta = 0.375 rd sin(theta) = 0.366273 dimensionless asin(sin(theta)) = 0.375 rd
Dealing with complex quantities is trivial. Here is the calculation of complex
impedance :
複數的數量也是一樣的。下面就是複數電阻的計算:
quantity<electric_potential,complex_type> v = complex_type(12.5,0.0)*volts; quantity<current,complex_type> i = complex_type(3.0,4.0)*amperes; quantity<resistance,complex_type> z = complex_type(1.5,-2.0)*ohms;
giving
V = (12.5,0) V I = (3,4) A Z = (1.5,-2) Ohm I*Z = (12.5,0) V
User-defined value types that support the appropriate arithmetic operations
are automatically supported as quantity value types. The operators that
are supported by default for quantity value types are unary plus, unary
minus, addition, subtraction, multiplication, division, equal-to, not-equal-to,
less-than, less-or-equal-to, greater-than, and greater-or-equal-to. Support
for rational powers and roots can be added by overloading the power_typeof_helper
and root_typeof_helper
classes. Here we implement a user-defined measurement
class that models a numerical measurement with an associated measurement
error and the appropriate algebra and demonstrates its use as a quantity
value type; the full code is found in measurement.hpp.
用戶自定義的數值類型如果支持這些算術運算,就可以自動地支持數量的運算。
數量的數值類型默認支持的運算有:取正、取負、加、減、乘、除、等於、不等於、小於、小於等於、大於和大於等於。
支持有理數次乘方開方可以通過添加power_typeofo_helper和root_typeof_helper類的特化(原文為overload,但應該是特化,而不是重載,譯者注)來實現。
我們定義一個類measurement表示數字的度量(numerical measurement),相關的度量錯誤和適當的代數運算,將它作為數量(quantity)的數值類型(value type);完整的代碼請見measurement.hpp。
Then, defining some measurement
quantity
variables
quantity<length,measurement<double> > u(measurement<double>(1.0,0.0)*meters), w(measurement<double>(4.52,0.02)*meters), x(measurement<double>(2.0,0.2)*meters), y(measurement<double>(3.0,0.6)*meters);
gives
x+y-w = 0.48(+/-0.632772) m w*x = 9.04(+/-0.904885) m^2 x/y = 0.666667(+/-0.149071) dimensionless
If we implement the overloaded helper classes for rational powers and roots
then we can also compute rational powers of measurement quantities :
如果我們特化(overload,譯者注)了乘方和開方的helper classes,就可以計算measurement數量的乘方了:
w*y^2/(u*x)^2 = 10.17(+/-3.52328) m^-1 w/(u*x)^(1/2) = 3.19612(+/-0.160431) dimensionless
This example demonstrates the various allowed conversions between SI and
CGS units. Defining some quantities
這個例子展示了SI和CGS單位之間的轉換。先定義幾個數量
quantity<si::length> L1 = quantity<si::length,int>(int(2.5)*si::meters); quantity<si::length,int> L2(quantity<si::length,double>(2.5*si::meters));
illustrates implicit conversion of quantities of different value types where
implicit conversion of the value types themselves is allowed. N.B. The conversion
from double to int is treated as an explicit conversion because there is
no way to emulate the exact behavior of the built-in conversion. Explicit
constructors allow conversions for two cases:
舉例說明了當數值類型之間可以隱式轉換的時候,數量才可以隱式轉換。N.B.
從double到int的轉換是顯式轉換,因為沒有辦法模仿內置轉換的準確行為。(???)
顯式構造可以支持以下兩種轉換:
quantity<si::length,int> L3 = static_cast<quantity<si::length,int> >(L1);
quantity<cgs::length> L4 = static_cast<quantity<cgs::length> >(L1);
giving the following output :
L1 = 2 m L2 = 2 m L3 = 2 m L4 = 200 cm L5 = 5 m L6 = 4 m L7 = 200 cm
A few more explicit unit system conversions :
幾個單位系統之間的顯式轉換:
quantity<si::volume> vs(1.0*pow<3>(si::meter)); quantity<cgs::volume> vc(vs); quantity<si::volume> vs2(vc); quantity<si::energy> es(1.0*si::joule); quantity<cgs::energy> ec(es); quantity<si::energy> es2(ec); quantity<si::velocity> v1 = 2.0*si::meters/si::second, v2(2.0*cgs::centimeters/cgs::second);
which produces the following output:
volume (m^3) = 1 m^3 volume (cm^3) = 1e+06 cm^3 volume (m^3) = 1 m^3 energy (joules) = 1 J energy (ergs) = 1e+07 erg energy (joules) = 1 J velocity (2 m/s) = 2 m s^-1 velocity (2 cm/s) = 0.02 m s^-1
This example demonstrates the use of boost::math::quaternion
as a value type for quantity
and the converse. For the first case, we first define specializations of
power_typeof_helper
and root_typeof_helper
for powers and roots, respectively:
這個例子展示了使用boost::math::quaternion(四元數)作為quantity的數值類型(value type) and the converse(???)。
對於第一種情況,我們首先為乘方和開方定義power_typeof_helper和root_typeof_helper的特化
/// specialize power typeof helper template<class Y,long N,long D> struct power_typeof_helper<boost::math::quaternion<Y>,static_rational<N,D> > { // boost::math::quaternion only supports integer powers BOOST_STATIC_ASSERT(D==1); typedef boost::math::quaternion< typename power_typeof_helper<Y,static_rational<N,D> >::type > type; static type value(const boost::math::quaternion<Y>& x) { return boost::math::pow(x,static_cast<int>(N)); } };
/// specialize root typeof helper template<class Y,long N,long D> struct root_typeof_helper<boost::math::quaternion<Y>,static_rational<N,D> > { // boost::math::quaternion only supports integer powers BOOST_STATIC_ASSERT(N==1); typedef boost::math::quaternion< typename root_typeof_helper<Y,static_rational<N,D> >::type > type; static type value(const boost::math::quaternion<Y>& x) { return boost::math::pow(x,static_cast<int>(D)); } };
We can now declare a quantity
of a quaternion :
typedef quantity<length,quaternion<double> > length_dimension; length_dimension L(quaternion<double>(4.0,3.0,2.0,1.0)*meters);
so that all operations that are defined in the quaternion
class behave correctly. If rational powers were defined for this class, it
would be possible to compute rational powers and roots with no additional
changes.
現在,我們定義於類quaternion上的操作符都可以正確執行了。
如果這個類定義了有理數乘方,那麼就可以直接計算乘方和開方了。
+L = (4,3,2,1) m -L = (-4,-3,-2,-1) m L+L = (8,6,4,2) m L-L = (0,0,0,0) m L*L = (2,24,16,8) m^2 L/L = (1,0,0,0) dimensionless L^3 = (-104,102,68,34) m^3
Now, if for some reason we preferred the quantity to be the value
type of the quaternion class
we would have :
如果我們將quantity作為quaternion的數值類型:
typedef quaternion<quantity<length> > length_dimension; length_dimension L(4.0*meters,3.0*meters,2.0*meters,1.0*meters);
Here, the unary plus and minus and addition and subtraction operators function
correctly. Unfortunately, the multiplication and division operations fail
because quaternion implements
them in terms of the *= and
/= operators, respectively,
which are incapable of representing the heterogeneous unit algebra needed
for quantities (an identical problem occurs with std::complex<T>,
for the same reason). In order to compute rational powers and roots, we need
to specialize power_typeof_helper
and root_typeof_helper
as follows:
現在,取正、取負、加和減的操作都是正確的。
但不幸的是,乘和除失敗了,因為quaternion是通過*=和/=實現的乘除。
這不能滿足數量的異構單位代數需要(同樣的問題也出現在std::complex<T>上)(???)
為了計算乘方和開方,我們需要特化power_typeof_helper和root_typeof_helper:
/// specialize power typeof helper for quaternion<quantity<Unit,Y> > template<class Unit,long N,long D,class Y> struct power_typeof_helper< boost::math::quaternion<quantity<Unit,Y> >, static_rational<N,D> > { typedef typename power_typeof_helper< Y, static_rational<N,D> >::type value_type; typedef typename power_typeof_helper< Unit, static_rational<N,D> >::type unit_type; typedef quantity<unit_type,value_type> quantity_type; typedef boost::math::quaternion<quantity_type> type; static type value(const boost::math::quaternion<quantity<Unit,Y> >& x) { const boost::math::quaternion<value_type> tmp = pow<static_rational<N,D> >(boost::math::quaternion<Y>( x.R_component_1().value(), x.R_component_2().value(), x.R_component_3().value(), x.R_component_4().value())); return type(quantity_type::from_value(tmp.R_component_1()), quantity_type::from_value(tmp.R_component_2()), quantity_type::from_value(tmp.R_component_3()), quantity_type::from_value(tmp.R_component_4())); } };
/// specialize root typeof helper for quaternion<quantity<Unit,Y> > template<class Unit,long N,long D,class Y> struct root_typeof_helper< boost::math::quaternion<quantity<Unit,Y> >, static_rational<N,D> > { typedef typename root_typeof_helper< Y, static_rational<N,D> >::type value_type; typedef typename root_typeof_helper< Unit, static_rational<N,D> >::type unit_type; typedef quantity<unit_type,value_type> quantity_type; typedef boost::math::quaternion<quantity_type> type; static type value(const boost::math::quaternion<quantity<Unit,Y> >& x) { const boost::math::quaternion<value_type> tmp = root<static_rational<N,D> >(boost::math::quaternion<Y>( x.R_component_1().value(), x.R_component_2().value(), x.R_component_3().value(), x.R_component_4().value())); return type(quantity_type::from_value(tmp.R_component_1()), quantity_type::from_value(tmp.R_component_2()), quantity_type::from_value(tmp.R_component_3()), quantity_type::from_value(tmp.R_component_4())); } };
giving:
+L = (4 m,3 m,2 m,1 m) -L = (-4 m,-3 m,-2 m,-1 m) L+L = (8 m,6 m,4 m,2 m) L-L = (0 m,0 m,0 m,0 m) L^3 = (-104 m^3,102 m^3,68 m^3,34 m^3)
This example demonstrates how to implement a replacement complex
class that functions correctly both as a quantity value type and as a quantity
container class, including heterogeneous multiplication and division operations
and rational powers and roots. Naturally, heterogeneous operations are only
supported on compilers that implement typeof.
The primary differences are that binary operations are not implemented using
the op=
operators and use the utility classes add_typeof_helper,
subtract_typeof_helper,
multiply_typeof_helper,
and divide_typeof_helper.
In addition, power_typeof_helper
and root_typeof_helper
are defined for both cases :
這個例子展示了如何使用替代的complex類作為quantity的數值類型或quantity的存儲類,包括異構的乘、除、乘方和開方。
通常,異構的乘和除只有當編譯器實現typeof的時候才被支持。
這些二元運算的主要差別在於不是使用op=操作符,而是使用工具類add_typeof_helper、subtract_typeof_helper、multiply_typeof_helper、divide_typeof_helper。
另外,power_typeof_helper和root_typeof_helper在兩種情況下都會定義:
namespace boost { namespace units { /// replacement complex class template<class T> class complex { public: typedef complex<T> this_type; complex(const T& r = 0,const T& i = 0) : r_(r),i_(i) { } complex(const this_type& source) : r_(source.r_),i_(source.i_) { } this_type& operator=(const this_type& source) { if (this == &source) return *this; r_ = source.r_; i_ = source.i_; return *this; } T& real() { return r_; } T& imag() { return i_; } const T& real() const { return r_; } const T& imag() const { return i_; } this_type& operator+=(const T& val) { r_ += val; return *this; } this_type& operator-=(const T& val) { r_ -= val; return *this; } this_type& operator*=(const T& val) { r_ *= val; i_ *= val; return *this; } this_type& operator/=(const T& val) { r_ /= val; i_ /= val; return *this; } this_type& operator+=(const this_type& source) { r_ += source.r_; i_ += source.i_; return *this; } this_type& operator-=(const this_type& source) { r_ -= source.r_; i_ -= source.i_; return *this; } this_type& operator*=(const this_type& source) { *this = *this * source; return *this; } this_type& operator/=(const this_type& source) { *this = *this / source; return *this; } private: T r_,i_; }; } } #if BOOST_UNITS_HAS_BOOST_TYPEOF #include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP() BOOST_TYPEOF_REGISTER_TEMPLATE(boost::units::complex, 1) #endif namespace boost { namespace units { template<class X> complex<typename unary_plus_typeof_helper<X>::type> operator+(const complex<X>& x) { typedef typename unary_plus_typeof_helper<X>::type type; return complex<type>(x.real(),x.imag()); } template<class X> complex<typename unary_minus_typeof_helper<X>::type> operator-(const complex<X>& x) { typedef typename unary_minus_typeof_helper<X>::type type; return complex<type>(-x.real(),-x.imag()); } template<class X,class Y> complex<typename add_typeof_helper<X,Y>::type> operator+(const complex<X>& x,const complex<Y>& y) { typedef typename boost::units::add_typeof_helper<X,Y>::type type; return complex<type>(x.real()+y.real(),x.imag()+y.imag()); } template<class X,class Y> complex<typename boost::units::subtract_typeof_helper<X,Y>::type> operator-(const complex<X>& x,const complex<Y>& y) { typedef typename boost::units::subtract_typeof_helper<X,Y>::type type; return complex<type>(x.real()-y.real(),x.imag()-y.imag()); } template<class X,class Y> complex<typename boost::units::multiply_typeof_helper<X,Y>::type> operator*(const complex<X>& x,const complex<Y>& y) { typedef typename boost::units::multiply_typeof_helper<X,Y>::type type; return complex<type>(x.real()*y.real() - x.imag()*y.imag(), x.real()*y.imag() + x.imag()*y.real()); // fully correct implementation has more complex return type // // typedef typename boost::units::multiply_typeof_helper<X,Y>::type xy_type; // // typedef typename boost::units::add_typeof_helper< // xy_type,xy_type>::type xy_plus_xy_type; // typedef typename // boost::units::subtract_typeof_helper<xy_type,xy_type>::type // xy_minus_xy_type; // // BOOST_STATIC_ASSERT((boost::is_same<xy_plus_xy_type, // xy_minus_xy_type>::value == true)); // // return complex<xy_plus_xy_type>(x.real()*y.real()-x.imag()*y.imag(), // x.real()*y.imag()+x.imag()*y.real()); } template<class X,class Y> complex<typename boost::units::divide_typeof_helper<X,Y>::type> operator/(const complex<X>& x,const complex<Y>& y) { // naive implementation of complex division typedef typename boost::units::divide_typeof_helper<X,Y>::type type; return complex<type>((x.real()*y.real()+x.imag()*y.imag())/ (y.real()*y.real()+y.imag()*y.imag()), (x.imag()*y.real()-x.real()*y.imag())/ (y.real()*y.real()+y.imag()*y.imag())); // fully correct implementation has more complex return type // // typedef typename boost::units::multiply_typeof_helper<X,Y>::type xy_type; // typedef typename boost::units::multiply_typeof_helper<Y,Y>::type yy_type; // // typedef typename boost::units::add_typeof_helper<xy_type, xy_type>::type // xy_plus_xy_type; // typedef typename boost::units::subtract_typeof_helper< // xy_type,xy_type>::type xy_minus_xy_type; // // typedef typename boost::units::divide_typeof_helper< // xy_plus_xy_type,yy_type>::type xy_plus_xy_over_yy_type; // typedef typename boost::units::divide_typeof_helper< // xy_minus_xy_type,yy_type>::type xy_minus_xy_over_yy_type; // // BOOST_STATIC_ASSERT((boost::is_same<xy_plus_xy_over_yy_type, // xy_minus_xy_over_yy_type>::value == true)); // // return complex<xy_plus_xy_over_yy_type>( // (x.real()*y.real()+x.imag()*y.imag())/ // (y.real()*y.real()+y.imag()*y.imag()), // (x.imag()*y.real()-x.real()*y.imag())/ // (y.real()*y.real()+y.imag()*y.imag())); } template<class Y> complex<Y> pow(const complex<Y>& x,const Y& y) { std::complex<Y> tmp(x.real(),x.imag()); tmp = std::pow(tmp,y); return complex<Y>(tmp.real(),tmp.imag()); } template<class Y> std::ostream& operator<<(std::ostream& os,const complex<Y>& val) { os << val.real() << " + " << val.imag() << " i"; return os; } /// specialize power typeof helper for complex<Y> template<class Y,long N,long D> struct power_typeof_helper<complex<Y>,static_rational<N,D> > { typedef complex< typename power_typeof_helper<Y,static_rational<N,D> >::type > type; static type value(const complex<Y>& x) { const static_rational<N,D> rat; const Y m = Y(rat.numerator())/Y(rat.denominator()); return boost::units::pow(x,m); } }; /// specialize root typeof helper for complex<Y> template<class Y,long N,long D> struct root_typeof_helper<complex<Y>,static_rational<N,D> > { typedef complex< typename root_typeof_helper<Y,static_rational<N,D> >::type > type; static type value(const complex<Y>& x) { const static_rational<N,D> rat; const Y m = Y(rat.denominator())/Y(rat.numerator()); return boost::units::pow(x,m); } }; /// specialize power typeof helper for complex<quantity<Unit,Y> > template<class Y,class Unit,long N,long D> struct power_typeof_helper<complex<quantity<Unit,Y> >,static_rational<N,D> > { typedef typename power_typeof_helper<Y,static_rational<N,D> >::type value_type; typedef typename power_typeof_helper<Unit,static_rational<N,D> >::type unit_type; typedef quantity<unit_type,value_type> quantity_type; typedef complex<quantity_type> type; static type value(const complex<quantity<Unit,Y> >& x) { const complex<value_type> tmp = pow<static_rational<N,D> >(complex<Y>(x.real().value(), x.imag().value())); return type(quantity_type::from_value(tmp.real()), quantity_type::from_value(tmp.imag())); } }; /// specialize root typeof helper for complex<quantity<Unit,Y> > template<class Y,class Unit,long N,long D> struct root_typeof_helper<complex<quantity<Unit,Y> >,static_rational<N,D> > { typedef typename root_typeof_helper<Y,static_rational<N,D> >::type value_type; typedef typename root_typeof_helper<Unit,static_rational<N,D> >::type unit_type; typedef quantity<unit_type,value_type> quantity_type; typedef complex<quantity_type> type; static type value(const complex<quantity<Unit,Y> >& x) { const complex<value_type> tmp = root<static_rational<N,D> >(complex<Y>(x.real().value(), x.imag().value())); return type(quantity_type::from_value(tmp.real()), quantity_type::from_value(tmp.imag())); } }; } // namespace units } // namespace boost
With this replacement complex
class, we can declare a complex variable :
使用這個替代的complex,我們定義一個複數變量:
typedef quantity<length,complex<double> > length_dimension; length_dimension L(complex<double>(2.0,1.0)*meters);
to get the correct behavior for all cases supported by quantity with a complex value type :
能看到使用complex作為數值類型的quantity支持的所有操作:
+L = 2 + 1 i m -L = -2 + -1 i m L+L = 4 + 2 i m L-L = 0 + 0 i m L*L = 3 + 4 i m^2 L/L = 1 + 0 i dimensionless L^3 = 2 + 11 i m^3 L^(3/2) = 2.56713 + 2.14247 i m^(3/2) 3vL = 1.29207 + 0.201294 i m^(1/3) (3/2)vL = 1.62894 + 0.520175 i m^(2/3)
and, similarly, complex with
a quantity
value type
類似的,用quantity作為數值類型的complex:
typedef complex<quantity<length> > length_dimension; length_dimension L(2.0*meters,1.0*meters);
gives
+L = 2 m + 1 m i -L = -2 m + -1 m i L+L = 4 m + 2 m i L-L = 0 m + 0 m i L*L = 3 m^2 + 4 m^2 i L/L = 1 dimensionless + 0 dimensionless i L^3 = 2 m^3 + 11 m^3 i L^(3/2) = 2.56713 m^(3/2) + 2.14247 m^(3/2) i 3vL = 1.29207 m^(1/3) + 0.201294 m^(1/3) i (3/2)vL = 1.62894 m^(2/3) + 0.520175 m^(2/3) i
This example provides an ad hoc performance test to verify that zero runtime
overhead is incurred when using quantity
in place of double. Note that
performance optimization and testing is not trivial, so some care must be
taken in profiling. It is also critical to have a compiler capable of optimizing
the many template instantiations and inline calls effectively to achieve
maximal performance. Zero overhead for this test has been verified using
gcc 4.0.1, and icc 9.0, 10.0, and 10.1 on Mac OS 10.4 and 10.5, and using
msvc 8.0 on Windows XP.
這個例子展示了一個專門的性能測試來說明用quantity代替double不會引入運行時開銷。
運行優化和測試不是微不足道的,所以在剖析時某些地方必須注意。
編譯器優化模板實例化的能力和inline調用的能力是很重要的。
零開銷已經在下面的編譯器和平台上被證明了:gcc 4.0.1, and icc 9.0, 10.0, and 10.1 on Mac OS 10.4 and 10.5, and using msvc 8.0 on Windows XP。
This example demonstrates the implementation of two non-SI units of length,
the nautical mile :
這個例子展示了兩個非SI長度單位的實現,海裡:
namespace nautical { struct length_base_unit : boost::units::base_unit<length_base_unit, length_dimension, 1> { static std::string name() { return "nautical mile"; } static std::string symbol() { return "nmi"; } }; typedef boost::units::make_system<length_base_unit>::type system; /// unit typedefs typedef unit<length_dimension,system> length; static const length mile,miles; } // namespace nautical // helper for conversions between nautical length and si length BOOST_UNITS_DEFINE_CONVERSION_FACTOR(nautical::length_base_unit, boost::units::si::meter_base_unit, double, 1.852e3);
and the imperial foot :
和英制的尺:
namespace imperial { struct length_base_unit : boost::units::base_unit<length_base_unit, length_dimension, 2> { static std::string name() { return "foot"; } static std::string symbol() { return "ft"; } }; typedef boost::units::make_system<length_base_unit>::type system; /// unit typedefs typedef unit<length_dimension,system> length; static const length foot,feet; } // imperial // helper for conversions between imperial length and si length BOOST_UNITS_DEFINE_CONVERSION_FACTOR(imperial::length_base_unit, boost::units::si::meter_base_unit, double, 1.0/3.28083989501312);
These units include conversions between themselves and the meter. Three functions
for computing radar beam height from radar range and the local earth radius
are defined. The first takes arguments in one system and returns a value
in the same system :
這些單位和米之間都存在著轉換關係。
三個通過雷達範圍和當地地球半徑計算雷達截面高度的函數被定義了。
第一個函數以一個單位系統為參數,返回同樣單位系統的值:
template<class System,typename T> quantity<unit<boost::units::length_dimension,System>,T> radar_beam_height(const quantity<unit<length_dimension,System>,T>& radar_range, const quantity<unit<length_dimension,System>,T>& earth_radius, T k = 4.0/3.0) { return quantity<unit<length_dimension,System>,T> (pow<2>(radar_range)/(2.0*k*earth_radius)); }
The second is similar, but is templated on return type, so that the arguments
are converted to the return unit system internally :
第二個函數類似,只是將返回值模板化了,這樣參數就在內部被轉換為返回值的單位系統:
template<class return_type,class System1,class System2,typename T> return_type radar_beam_height(const quantity<unit<length_dimension,System1>,T>& radar_range, const quantity<unit<length_dimension,System2>,T>& earth_radius, T k = 4.0/3.0) { // need to decide which system to use for calculation const return_type rr(radar_range), er(earth_radius); return return_type(pow<2>(rr)/(2.0*k*er)); }
Finally, the third function is an empirical approximation that is only valid
for radar ranges specified in nautical miles, returning beam height in feet.
This function uses the heterogeneous unit of nautical miles per square root
of feet to ensure dimensional correctness :
最後,第三個函數是一個經驗公式(empirical approximation不會譯),只有當雷達範圍的單位是海裡,返回的截面高度是英尺時才正確。
這個函數使用了異構的單位,nautical miles per square root of feet:
quantity<imperial::length> radar_beam_height(const quantity<nautical::length>& range) { return quantity<imperial::length> (pow<2>(range/(1.23*nautical::miles/root<2>(imperial::feet)))); }
With these, we can compute radar beam height in various unit systems :
這樣,我們可以用不同的單位系統來計算雷達截面高度:
const quantity<nautical::length> radar_range(300.0*miles); const quantity<si::length> earth_radius(6371.0087714*kilo*meters); const quantity<si::length> beam_height_1(radar_beam_height(quantity<si::length>(radar_range),earth_radius)); const quantity<nautical::length> beam_height_2(radar_beam_height(radar_range,quantity<nautical::length>(earth_radius))); const quantity<si::length> beam_height_3(radar_beam_height< quantity<si::length> >(radar_range,earth_radius)); const quantity<nautical::length> beam_height_4(radar_beam_height< quantity<nautical::length> >(radar_range,earth_radius));
giving
radar range : 300 nmi earth radius : 6.37101e+06 m beam height 1 : 18169.7 m beam height 2 : 9.81085 nmi beam height 3 : 18169.7 m beam height 4 : 9.81085 nmi beam height approx : 59488.4 ft beam height approx : 18132.1 m
Mixed units and mixed unit conversions.
混合單位及其轉換。
This code:
quantity<si::length> L(1.5*si::meter); quantity<cgs::mass> M(1.0*cgs::gram); std::cout << L << std::endl << M << std::endl << L*M << std::endl << L/M << std::endl << std::endl; std::cout << 1.0*si::meter*si::kilogram/pow<2>(si::second) << std::endl << 1.0*si::meter*si::kilogram/pow<2>(si::second)/si::meter << std::endl << std::endl; std::cout << 1.0*cgs::centimeter*si::kilogram/pow<2>(si::second) << std::endl << 1.0*cgs::centimeter*si::kilogram/pow<2>(si::second)/si::meter << std::endl << std::endl;
gives
1.5 m 1 g 1.5 m g 1.5 m g^-1 1 N 1 kg s^-2 1 cm kg s^-2 1 cm m^-1 kg s^-2
Arbitrary conversions also work:
任意的轉換也可以工作:
quantity<si::area> A(1.5*si::meter*cgs::centimeter); std::cout << 1.5*si::meter*cgs::centimeter << std::endl << A << std::endl << std::endl;
yielding
1.5 cm m 0.015 m^2
This example demonstrates using of absolute temperatures and relative temperature
differences in Fahrenheit and converting between these and the Kelvin temperature
scale. This issue touches on some surprisingly deep mathematical concepts
(see Wikipedia
for a basic review), but for our purposes here, we will simply observe that
it is important to be able to differentiate between an absolute temperature
measurement and a measurement of temperature difference. This is accomplished
by using the absolute
wrapper class.
這個例子展示了使用絕對溫度和華氏相對溫度,以及與開爾文溫度之間的轉換。(???)
這部分會涉及到一些很深的數學概念(見Wikipedia),
但對於我們來說,只是知道區分一個絕對的溫度度量和溫度差異度量是非常重要的。
這是通過使用外覆類absolute來實現的。
First we define a system using the predefined fahrenheit base unit:
首先,我們使用預定義的華氏基礎單位來定義一個系統:
typedef temperature::fahrenheit_base_unit::unit_type temperature; typedef get_system<temperature>::type system; BOOST_UNITS_STATIC_CONSTANT(degree,temperature); BOOST_UNITS_STATIC_CONSTANT(degrees,temperature);
Now we can create some quantities:
然後創建一個數量:
quantity<absolute<fahrenheit::temperature> > T1p( 32.0*absolute<fahrenheit::temperature>()); quantity<fahrenheit::temperature> T1v( 32.0*fahrenheit::degrees); quantity<absolute<si::temperature> > T2p(T1p); quantity<si::temperature> T2v(T1v);
Note the use of absolute
to wrap a unit. The resulting output is:
注意absolute的使用。輸出如下:
{ 32 } F { 273.15 } K { 273.15 } K [ 32 ] F [ 17.7778 ] K [ 17.7778 ] K
(runtime_conversion_factor.cpp)
The Boost.Units library does not require that the conversion factors be compile
time constants, as is demonstrated in this example:
Boost.Units庫並不要求轉換因子是編譯時常量,就像下面例子展示的:
using boost::units::base_dimension; using boost::units::base_unit; static const long currency_base = 1; struct currency_base_dimension : base_dimension<currency_base_dimension, 1> {}; typedef currency_base_dimension::dimension_type currency_type; template<long N> struct currency_base_unit : base_unit<currency_base_unit<N>, currency_type, currency_base + N> {}; typedef currency_base_unit<0> us_dollar_base_unit; typedef currency_base_unit<1> euro_base_unit; typedef us_dollar_base_unit::unit_type us_dollar; typedef euro_base_unit::unit_type euro; // an array of all possible conversions double conversion_factors[2][2] = { {1.0, 1.0}, {1.0, 1.0} }; double get_conversion_factor(long from, long to) { return(conversion_factors[from][to]); } void set_conversion_factor(long from, long to, double value) { conversion_factors[from][to] = value; conversion_factors[to][from] = 1.0 / value; } BOOST_UNITS_DEFINE_CONVERSION_FACTOR_TEMPLATE((long N1)(long N2), currency_base_unit<N1>, currency_base_unit<N2>, double, get_conversion_factor(N1, N2));
It is also possible to define base units that have derived rather than base
dimensions:
同樣可以不從基礎量綱派生定義基礎單位(???):
struct imperial_gallon_tag : base_unit<imperial_gallon_tag, volume_dimension, 1> { }; typedef make_system<imperial_gallon_tag>::type imperial; typedef unit<volume_dimension,imperial> imperial_gallon; struct us_gallon_tag : base_unit<us_gallon_tag, volume_dimension, 2> { }; typedef make_system<us_gallon_tag>::type us; typedef unit<volume_dimension,us> us_gallon;
If a unit has a special name and/or symbol, the free functions name_string and symbol_string
can be overloaded directly.
如果一個單位有特殊的名字(name)和/或符號(symbol),自由函數name_string和symbol_string可以被直接重載。
std::string name_string(const cgs::force&) { return "dyne"; } std::string symbol_string(const cgs::force&) { return "dyn"; }
In this case, any unit that reduces to the overloaded unit will be output
with the replacement symbol.
這樣,任何可以化簡到重載單位的單位輸出都可以用這個替代符號。
Special names and symbols for the SI and CGS unit systems are found in boost/units/systems/si/io.hpp
and boost/units/systems/cgs/io.hpp,
respectively. If these headers are not included, the output will simply follow
default rules using the appropriate fundamental dimensions. Note that neither
of these functions is defined for quantities because doing so would require
making assumptions on how the corresponding value type should be formatted.
SI和CGS單位系統專門的名字和符號可以分別在boost/units/systems/si/io.hpp和boost/units/systems/cgs/io.hpp文件中找到。
如果這些頭文件沒有被包含,輸出會簡單地使用基本量綱(fundamental dimension)的規則輸出。
這些函數都沒有為數量(quantity)定義,因為如果那麼做的話就會要求假定相關的數值類型(value type)是如何輸出的。
Three ostream formatters,
symbol_format, name_format, and typename_format
are provided for convenience. These select the textual representation of
units provided by symbol_string
or name_string in the first
two cases, while the latter returns a demangled typename for debugging purposes.
Formatting of scaled unit is also done correctly.
三個ostream格式程序(symbol_format、name_format、typename_format)提供了方便。
前兩個使用symbol_string或name_string提供的字符串,後一個提供了調試用的重整過的類型定義。
成比例單位同樣能夠被正確地格式化。
This code demonstrates the use of the conversion_factor
free function to determine the scale factor between two units.
這個例子展示了使用自由函數conversion_factor來確定兩個單位之間的比例係數。
double dyne_to_newton = conversion_factor(cgs::dyne,si::newton); std::cout << dyne_to_newton << std::endl; double force_over_mass_conversion = conversion_factor(si::newton/si::kilogram,cgs::dyne/cgs::gram); std::cout << force_over_mass_conversion << std::endl; double momentum_conversion = conversion_factor(cgs::momentum(),si::momentum()); std::cout << momentum_conversion << std::endl; double momentum_over_mass_conversion = conversion_factor(si::momentum()/si::mass(),cgs::momentum()/cgs::gram); std::cout << momentum_over_mass_conversion << std::endl; double acceleration_conversion = conversion_factor(cgs::gal,si::meter_per_second_squared); std::cout << acceleration_conversion << std::endl;
Produces
1e-005 100 1e-005 100 0.01
This example shows how to implement an interface that allow different units
at runtime while still maintaining type safety for internal calculations.
這個例子展示了如何實現一個接口,能夠在運行時接受不同的單位並維持類型安全和內部和計算。
namespace { using namespace boost::units; using imperial::foot_base_unit; std::map<std::string, quantity<si::length> > known_units; } quantity<si::length> calculate(const quantity<si::length>& t) { return(boost::units::hypot(t, 2.0 * si::meters)); } int main() { known_units["meter"] = 1.0 * si::meters; known_units["centimeter"] = .01 * si::meters; known_units["foot"] = conversion_factor(foot_base_unit::unit_type(), si::meter) * si::meter; std::string output_type("meter"); std::string input; while((std::cout << "> ") && (std::cin >> input)) { if(!input.empty() && input[0] == '#') { std::getline(std::cin, input); } else if(input == "exit") { break; } else if(input == "help") { std::cout << "type \"exit\" to exit\n" "type \"return 'unit'\" to set the return units\n" "type \"'number' 'unit'\" to do a simple calculation" << std::endl; } else if(input == "return") { if(std::cin >> input) { if(known_units.find(input) != known_units.end()) { output_type = input; std::cout << "Done." << std::endl; } else { std::cout << "Unknown unit \"" << input << "\"" << std::endl; } } else { break; } } else { try { double value = boost::lexical_cast<double>(input); if(std::cin >> input) { if(known_units.find(input) != known_units.end()) { std::cout << static_cast<double>( calculate(value * known_units[input]) / known_units[output_type]) << ' ' << output_type << std::endl; } else { std::cout << "Unknown unit \"" << input << "\"" << std::endl; } } else { break; } } catch(...) { std::cout << "Input error" << std::endl; } } } }
The header boost/units/lambda.hpp
provides overloads and specializations needed to make Boost.Units usable
with the Boost.Lambda library.
頭文件boost/units/lambda.hpp中提供了Boost.Units被Boost.Lambda使用所需的重載和特化。
int main(int argc, char **argv) { using namespace std; namespace bl = boost::lambda; namespace bu = boost::units; namespace si = boost::units::si; //////////////////////////////////////////////////////////////////////// // Mechanical example: linear accelerated movement //////////////////////////////////////////////////////////////////////// // Initial condition variables for acceleration, speed, and displacement bu::quantity<si::acceleration> a = 2.0 * si::meters_per_second_squared; bu::quantity<si::velocity> v = 1.0 * si::meters_per_second; bu::quantity<si::length> s0 = 0.5 * si::meter; // Displacement over time boost::function<bu::quantity<si::length> (bu::quantity<si::time>) > s = 0.5 * bl::var(a) * bl::_1 * bl::_1 + bl::var(v) * bl::_1 + bl::var(s0); cout << "Linear accelerated movement:" << endl << "a = " << a << ", v = " << v << ", s0 = " << s0 << endl << "s(1.0 * si::second) = " << s(1.0 * si::second) << endl << endl; // Change initial conditions a = 1.0 * si::meters_per_second_squared; v = 2.0 * si::meters_per_second; s0 = -1.5 * si::meter; cout << "a = " << a << ", v = " << v << ", s0 = " << s0 << endl << "s(1.0 * si::second) = " << s(1.0 * si::second) << endl << endl; //////////////////////////////////////////////////////////////////////// // Electrical example: oscillating current //////////////////////////////////////////////////////////////////////// // Constants for the current amplitude, frequency, and offset current const bu::quantity<si::current> iamp = 1.5 * si::ampere; const bu::quantity<si::frequency> f = 1.0e3 * si::hertz; const bu::quantity<si::current> i0 = 0.5 * si::ampere; // The invocation of the sin function needs to be postponed using // bind to specify the oscillation function. A lengthy static_cast // to the function pointer referencing boost::units::sin() is needed // to avoid an "unresolved overloaded function type" error. boost::function<bu::quantity<si::current> (bu::quantity<si::time>) > i = iamp * bl::bind(static_cast<bu::dimensionless_quantity<si::system, double>::type (*)(const bu::quantity<si::plane_angle>&)>(bu::sin), 2.0 * pi * si::radian * f * bl::_1) + i0; cout << "Oscillating current:" << endl << "iamp = " << iamp << ", f = " << f << ", i0 = " << i0 << endl << "i(1.25e-3 * si::second) = " << i(1.25e-3 * si::second) << endl << endl; //////////////////////////////////////////////////////////////////////// // Geometric example: area calculation for a square //////////////////////////////////////////////////////////////////////// // Length constant const bu::quantity<si::length> l = 1.5 * si::meter; // Again an ugly static_cast is needed to bind pow<2> to the first // function argument. boost::function<bu::quantity<si::area> (bu::quantity<si::length>) > A = bl::bind(static_cast<bu::quantity<si::area> (*)(const bu::quantity<si::length>&)>(bu::pow<2>), bl::_1); cout << "Area of a square:" << endl << "A(" << l <<") = " << A(l) << endl << endl; //////////////////////////////////////////////////////////////////////// // Thermal example: temperature difference of two absolute temperatures //////////////////////////////////////////////////////////////////////// // Absolute temperature constants const bu::quantity<bu::absolute<si::temperature> > Tref = 273.15 * bu::absolute<si::temperature>(); const bu::quantity<bu::absolute<si::temperature> > Tamb = 300.00 * bu::absolute<si::temperature>(); boost::function<bu::quantity<si::temperature> (bu::quantity<bu::absolute<si::temperature> >, bu::quantity<bu::absolute<si::temperature> >)> dT = bl::_2 - bl::_1; cout << "Temperature difference of two absolute temperatures:" << endl << "dT(" << Tref << ", " << Tamb << ") = " << dT(Tref, Tamb) << endl << endl; return 0; }