[Units] unary minus on units is counter intuitive (or a bug)
Hi, I found this counterintuitive result of applying the unary minus operator to a unit "- si::meter" is the same as "si::meter" (or any other unit). or worst std::cout << - si::meter*1. << std::endl; // prints 1*m i.e. the minus is explicitly ignored. The root of the problem is this curious line in boost/units/unit.hpp /// unit runtime unary minus template<class Dim,class System> typename unary_minus_typeof_helper< unit<Dim,System> >::type operator-(const unit<Dim,System>&) { typedef typename unary_minus_typeof_helper< unit<Dim,System>
::type type;
return type(); } a more intuitive result (but not completely elegant) could be attained by defining instead: template<class Dimension, class System> quantity<unit<Dim, System> > operator-(unit<Dim, System> const& u){ return -1.*u; } This is a definition that I could add BUT it will conflict with the original definition. Another way, could be to specialize unary_minus_typeof_helper<...>::type to be something that is not a unit but holds the negative "direction" and the cast to Type(-1)*unit quantity but it seems overly complicated for the purpose. Note aside: Why would I need "minus units" in the first place? The original code that produced the confusion was - kB*T which appears commonly in physics. kB is the "atomic" unit of entropy/ specific heat (atomic::heat_capacity) and T is a temperature quantity. This was parsed as (-kB)*T and then as kB*T i.e. the minus sign was ignored all together. In my opinion unary operator- should not be defined for units in the first place. The current definition only makes things worst, by creating a counter intuitive result AND making the fix conflict with the definition. Thoughts? Thank you, Alfredo
I found this counterintuitive result of applying the unary minus operator to a unit
"- si::meter" is the same as "si::meter" (or any other unit).
or worst
std::cout << - si::meter*1. << std::endl; // prints 1*m
i.e. the minus is explicitly ignored. The root of the problem is this curious line in boost/units/unit.hpp
I agree this is counterintuitive - the intent was that the unary operators should not affect units.
a more intuitive result (but not completely elegant) could be attained by defining instead:
template<class Dimension, class System> quantity<unit<Dim, System> > operator-(unit<Dim, System> const& u){ return -1.*u; }
This would be inconsistent with library architecture...
Note aside: Why would I need "minus units" in the first place? The original code that produced the confusion was
- kB*T
which appears commonly in physics. kB is the "atomic" unit of entropy/ specific heat (atomic::heat_capacity) and T is a temperature quantity. This was parsed as
(-kB)*T and then as kB*T
i.e. the minus sign was ignored all together.
In my opinion unary operator- should not be defined for units in the first place. The current definition only makes things worst, by creating a counter intuitive result AND making the fix conflict with the definition.
I think you're right that it is more correct for those operators to be undefined in this case. Try the following and see if it works for you : in unit.hpp make the following change to lines 102-116 : /// unit unary plus typeof helper /// INTERNAL ONLY //template<class Dim,class System> //struct unary_plus_typeof_helper< unit<Dim,System> > //{ // typedef unit<Dim,System> type; //}; /// unit unary minus typeof helper /// INTERNAL ONLY //template<class Dim,class System> //struct unary_minus_typeof_helper< unit<Dim,System> > //{ // typedef unit<Dim,System> type; //}; In quantity.hpp make the following change to lines 559-579 : /// specialize unary plus typeof helper /// INTERNAL ONLY template<class Unit,class Y> struct unary_plus_typeof_helper< quantity<Unit,Y> > { typedef typename unary_plus_typeof_helper<Y>::type value_type; // typedef typename unary_plus_typeof_helper<Unit>::type unit_type; typedef Unit unit_type; typedef quantity<unit_type,value_type> type; }; /// specialize unary minus typeof helper /// INTERNAL ONLY template<class Unit,class Y> struct unary_minus_typeof_helper< quantity<Unit,Y> > { typedef typename unary_minus_typeof_helper<Y>::type value_type; // typedef typename unary_minus_typeof_helper<Unit>::type unit_type; typedef Unit unit_type; typedef quantity<unit_type,value_type> type; }; This should result in a compile error in your code at -kB*T which is certainly better than a silent error. If that works, we can commit the changes to trunk. Matthias
2010/11/30 Matthias Schabel <boost@schabel-family.org>:
I found this counterintuitive result of applying the unary minus operator to a unit
"- si::meter" is the same as "si::meter" (or any other unit).
or worst
std::cout << - si::meter*1. << std::endl; // prints 1*m
i.e. the minus is explicitly ignored. The root of the problem is this curious line in boost/units/unit.hpp
I agree this is counterintuitive - the intent was that the unary operators should not affect units.
a more intuitive result (but not completely elegant) could be attained by defining instead:
template<class Dimension, class System> quantity<unit<Dim, System> > operator-(unit<Dim, System> const& u){ return -1.*u; }
This would be inconsistent with library architecture...
it seems so, but why exactly? because it commits to a certain internal representation (i.e. double)? (after all it is the default representation chosen by the library). Arguably, I think it is not that bad to define operator- or operator+ on units to return a quantity. In any case at least with the changes one is free to define such a function. On the other hand being forced to type -1.*si::meter or 1.*si::meter is not that bad either.
Note aside: Why would I need "minus units" in the first place? The original code that produced the confusion was
- kB*T
which appears commonly in physics. kB is the "atomic" unit of entropy/ specific heat (atomic::heat_capacity) and T is a temperature quantity. This was parsed as
(-kB)*T and then as kB*T
i.e. the minus sign was ignored all together.
In my opinion unary operator- should not be defined for units in the first place. The current definition only makes things worst, by creating a counter intuitive result AND making the fix conflict with the definition.
I think you're right that it is more correct for those operators to be undefined in this case.
yes, I think unary minus shuould be undefined for units. After all units, as mathematical objects, are quite weird. (see http://arxiv.org/abs/0710.1313).
Try the following and see if it works for you : in unit.hpp make the following change to lines 102-116 : /// unit unary plus typeof helper /// INTERNAL ONLY //template<class Dim,class System> //struct unary_plus_typeof_helper< unit<Dim,System> > //{ // typedef unit<Dim,System> type; //}; /// unit unary minus typeof helper /// INTERNAL ONLY //template<class Dim,class System> //struct unary_minus_typeof_helper< unit<Dim,System> > //{ // typedef unit<Dim,System> type; //}; In quantity.hpp make the following change to lines 559-579 : /// specialize unary plus typeof helper /// INTERNAL ONLY template<class Unit,class Y> struct unary_plus_typeof_helper< quantity<Unit,Y> > { typedef typename unary_plus_typeof_helper<Y>::type value_type; // typedef typename unary_plus_typeof_helper<Unit>::type unit_type; typedef Unit unit_type; typedef quantity<unit_type,value_type> type; }; /// specialize unary minus typeof helper /// INTERNAL ONLY template<class Unit,class Y> struct unary_minus_typeof_helper< quantity<Unit,Y> > { typedef typename unary_minus_typeof_helper<Y>::type value_type; // typedef typename unary_minus_typeof_helper<Unit>::type unit_type; typedef Unit unit_type; typedef quantity<unit_type,value_type> type; }; This should result in a compile error in your code at -kB*T which is certainly better than a silent error. If that works, we can commit the changes to trunk.
thank you, I made the changes and it works, that is, this program doesn't compile: #include<boost/units/systems/si.hpp> int main(){ using namespace boost::units; std::clog << -2.*si::meter << std::endl; //compiles std::clog << -si::meter*2. << std::endl; //compile error std::clog << +2.*si::meter << std::endl; //compiles std::clog << +si::meter*2. << std::endl; //compile error return 0; } Therefore, I believe you can commit the changes. Thank you, Alfredo
participants (3)
-
alfC
-
Alfredo Correa
-
Matthias Schabel