Boost logo

Boost Users :

Subject: Re: [Boost-users] [Units] unary minus on units is counter intuitive (or a bug)
From: Alfredo Correa (alfredo.correa_at_[hidden])
Date: 2010-12-03 05:24:04


2010/11/30 Matthias Schabel <boost_at_[hidden]>:
> 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


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net