c++ - 使用 Boost.Units 定义百分比

标签 c++ boost units-of-measurement

我想用 Boost.Units 实现一个百分比单位,以便无量纲量(如比率)可以表示为百分比。我已经成功实现了质量密度单位之间的转换,但对于无量纲单位却不起作用。这是我的代码(假设 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);

“克每毫升”部分按预期工作:我可以编译此代码(假设也使用命名空间 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++ 输出:没有调用“conversion_factor(..., ...)”的匹配函数

这与 dimensionless_type 似乎是类型列表结束标记这一事实有关吗?

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

最佳答案

Is this related to the fact that dimensionless_type seems to be a marker for end of typelists?

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

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

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

因此,如果您说您的百分比将是无量纲的,但与原始的无量纲不同,那么您就违背了上述概念。因此,您无法在系统内定义 2 个无量纲维度。

Any help or suggestion would be greatly appreciated.

我在这里看到 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);
    }
    

关于c++ - 使用 Boost.Units 定义百分比,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19160872/

相关文章:

c++ - "mutex"和 "lock"有什么区别?

python - 两个英制长度之差

android - 获取电池温度和结果单位

c++ - 将异常类与主类放在同一个文件中是一种不好的形式吗

c++ - 从套接字接收后出现 SEGFAULT

c++ - 每帧在 shared_ptr 中动态分配图像

c++ - BOOST_STATIC_ASSERT 能否给出自定义的编译错误字符串?

python - 使用数量时如何指定单位的标量乘数?

c++ - 混合 C 和 C++ 时出错

c# - 如何从 C# 使用 C++ dll