【问题标题】:Using Boost.Units to define percentage使用 Boost.Units 定义百分比
【发布时间】:2013-10-10 06:00:55
【问题描述】:

我想用 Boost.Units 实现一个percent 单位,以便可以将无量纲量(如比率)表示为百分比。我已经成功实现了质量密度单位之间的转换,但同样不适用于无量纲单位。这是我的代码(假设using namespace boost::units;):

//
// gram per milliliter (g mL^-1)
//
namespace my {
        struct gram_per_milliliter_base_unit :
                base_unit<gram_per_milliliter_base_unit, mass_density_dimension, 1>
        {
                static std::string name() {return "gram per milliliter";}
                static std::string symbol() {return "g mL^-1";}
        };
        typedef gram_per_milliliter_base_unit::unit_type gram_per_milliliter_unit;
        BOOST_UNITS_STATIC_CONSTANT(gram_per_milliliter, gram_per_milliliter_unit);
        BOOST_UNITS_STATIC_CONSTANT(grams_per_milliliter, gram_per_milliliter_unit);
}
BOOST_UNITS_DEFINE_CONVERSION_FACTOR(
        my::gram_per_milliliter_base_unit, si::mass_density, double, 1.0e3
); // 1 g mL^-1 == 1e3 kg m^-3 (SI unit)
BOOST_UNITS_DEFAULT_CONVERSION(my::gram_per_milliliter_base_unit, si::mass_density);

//
// percentage (%)
//
namespace my {
        struct percent_base_unit :
                base_unit<percent_base_unit, dimensionless_type, 2>
        {
                static std::string name() {return "percent";}
                static std::string symbol() {return "%";}
        };
        typedef percent_base_unit::unit_type percent_unit;
        BOOST_UNITS_STATIC_CONSTANT(percent, percent_unit);
}
BOOST_UNITS_DEFINE_CONVERSION_FACTOR(
        my::percent_base_unit, si::dimensionless, double, 1.0e-2
); // 1 % == 1e-2 (SI dimensionless unit)
BOOST_UNITS_DEFAULT_CONVERSION(my::percent_base_unit, si::dimensionless);

“克每毫升”部分按预期工作:我可以编译这段代码(假设 using namespace my; 也是如此):

quantity<gram_per_milliliter_unit> q1my(3*grams_per_milliliter);
quantity<si::mass_density> q1si(q1my);
quantity<gram_per_milliliter_unit> q1back(q1si);

但以下两种转换均无法编译:

quantity<percent_unit> q2my(3*percent);
quantity<si::dimensionless> q2si(q2my);
quantity<percent_unit> q2back(q2si);

G++ 输出:no matching function for call to 'conversion_factor(..., ...)'.

这是否与 dimensionless_type 似乎是类型列表结束的标记有关?

任何帮助或建议将不胜感激。谢谢。

【问题讨论】:

    标签: c++ boost units-of-measurement


    【解决方案1】:

    这是否与 dimensionless_type 似乎是类型列表结束的标记有关?

    有点。 dimensionless_type 隐式包含在每个测量系统中,并以相同的方式从它们中提取,请参阅boost/units/dimensionless_units.hpp

    在您的“百分比”示例中,想想您的新测量系统将是什么,以及如何根据通常的升压单位规则指定它:

    namespace my {
        ... // define your own unit tag types
        typedef make_system</* your system's units with dimensions*/>::type system;
        ... // unit typedefs, consts, etc.
    }
    

    因此,如果您说您的百分比将是无量纲的,但与原来的无量纲不同,那么您就是在挑战上述概念。因此,您不能在系统中定义 2 个无量纲维度。

    任何帮助或建议将不胜感激。

    我在这里看到 3 个选项:

    1. 覆盖基础数量类型并接受 ctor 中的百分比或常规数字。 见http://www.boost.org/doc/libs/release/doc/html/boost_units/Examples.html#boost_units.Examples.UDTExample

    2. 如果您想以百分比显示内容,可以尝试使用自动缩放功能(我自己从未做过,但也有一个示例 -- http://www.boost.org/doc/libs/release/doc/html/boost_units/Examples.html#boost_units.Examples.autoscale)。

    3. 您可以创建一个特殊的自定义维度“百分比”,并显式转换为百分比数量/从百分比数量转换。这可能最接近您的原始意图,但不会总是发生自动转换,因为该库不是为“无量纲量的维度分析”而设计的。如果您尝试强制系统进行自动转换,您会看到结果有多难看:

      //
      // percentage (%)
      //
      namespace my {
          struct percent_base_dimension : 
              base_dimension<percent_base_dimension, 1> {};
          typedef percent_base_dimension::dimension_type percent_type;
      
          struct percent_base_unit :
              base_unit<percent_base_unit, percent_type, 1>
          {
              static std::string name() {return "percent";}
              static std::string symbol() {return "%";}
          };
          typedef make_system<percent_base_unit>::type system;
          typedef percent_base_unit::unit_type percent_unit;
          BOOST_UNITS_STATIC_CONSTANT(percent, percent_unit);
      }
      
      namespace boost { namespace units {
      
      template<class T0, class T1>
      struct conversion_helper<quantity<my::percent_unit, T0>, quantity<si::dimensionless, T1> >
      {
          static quantity<si::dimensionless, T1> convert(const quantity<my::percent_unit, T0>& source)
          {
              return(quantity<si::dimensionless, T1>::from_value(1e-2 * source.value()));
          }
      };
      
      template<class T0, class T1>
      struct conversion_helper<quantity<si::dimensionless, T0>, quantity<my::percent_unit, T1> >
      {
          static quantity<my::percent_unit, T1> convert(const quantity<si::dimensionless, T0>& source)
          {
              return(quantity<my::percent_unit, T1>::from_value(1e+2 * source.value()));
          }
      };
      
      } }
      
      int main()
      {
          using namespace my;
      
          quantity<percent_unit> q2my(3*percent);
      
          //quantity<si::dimensionless> q2si(q2my);
          //The converter won't be picked up due to an explicit disable_if in quantity.hpp:
          // typename boost::disable_if<detail::is_dimensionless_system<System2> >::type* = 0
          //so we manually force the conversion here:
          auto conv = conversion_helper<quantity<percent_unit>, quantity<si::dimensionless> >::convert;
          quantity<si::dimensionless> q2si(conv(q2my));
      
          quantity<percent_unit> q2back(q2si);
      
          std::cout 
              << "q2my: " << q2my << std::endl
              << "q2si: " << q2si << std::endl
              << "q2back: " << q2back << std::endl
              ;
      }
      

      所以最好手动完成,比如

      namespace my {
          template<class Y>
          quantity<si::dimensionless, Y> units(const quantity<percent_unit, Y>& source)
          {
              return(quantity<si::dimensionless, Y>::from_value(1e-2 * source.value()));
          }
      
          template<class Y>
          quantity<percent_unit, Y> percentage(const quantity<si::dimensionless, Y>& source)
          {
              return(quantity<percent_unit, Y>::from_value(1e+2 * source.value()));
          }
      
      }
      
      int main()
      {
          using namespace my;
      
          quantity<percent_unit> q2my(3*percent);
          quantity<si::dimensionless> q2si(my::units(q2my));
          quantity<percent_unit> q2back(my::percentage(q2si));
      
          std::cout 
              << "q2my: " << q2my << std::endl
              << "q2si: " << q2si << std::endl
              << "q2back: " << q2back << std::endl
              ;
      }
      

      或者,更好的是,利用类型检查的好处(所以你只能在此处的转换因子中犯错误):

      template<class Y>
      quantity<si::dimensionless, Y> units(const quantity<percent_unit, Y>& source)
      {
          return quantity<si::dimensionless, Y>(1e-2 * source / percent);
      }
      
      template<class Y>
      quantity<percent_unit, Y> percentage(const quantity<si::dimensionless, Y>& source)
      {
          return quantity<percent_unit, Y>(1e+2 * source * percent);
      }
      

    【讨论】:

    • 多么棒的答案!谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-02
    • 1970-01-01
    • 2019-01-14
    • 1970-01-01
    • 2015-12-10
    相关资源
    最近更新 更多