Использование Boost.Units для определения процента

Я хотел бы реализовать percent единицы с 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);

Секция «грамм на миллилитр» работает как положено: я могу скомпилировать этот код (при условии 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 кажется маркером для конца списков типов?

Любая помощь или предложение будет принята с благодарностью. Спасибо.

3

Решение

Связано ли это с тем фактом, что размерный_каталог является маркером конца списков типов?

Вроде, как бы, что-то вроде. 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. Переопределите базовый тип количества и примите либо процентное, либо обычное число в ктор.
    Увидеть 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);
    }
    
5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]