Re: [boost] [Review] Quantitative Units library review begins today March 26

AMDG "Eric Lemings" wrote:
<snip>
Undoubtedly, the scope of this library should include the dimensional analysis framework which defines the fundamental types/metatypes (e.g. dimension, unit, quantity), their semantics, and their operations. It should also include only the most common dimensions such as the physical dimensions (time, length, mass, etc.) and allow, at least, other universal dimensions (e.g. currency). Other, more specialized dimensions should be defined elsewhere.
That was one of out goals. The units that we have are implemented solely in terms of the dimension/unit/quantity framework.
The scope should not include any facilities for input and output. It should also not prevent such support however. (Input and output of units and measures can be handled by extending C++ locales with new facets. C++ locales already deal with I/O of time and money. More about internalization and localization below.)
Input is definitely beyond the scope of the library. We have implemented primitive output.... On further thought, we should probably just have an output operator for quantity that prints the value_type then the unit and leave unit output alone.
The scope should not classify units of measurement into exclusionary systems. True, many programs/programmers will have restrictions that prevent/exclude use of any units of measurement outside a well-known set of units but the other extreme is also quite possible. A program may allow arithmetic/conversions between any and all units known to the development/operating environment. In such cases, the programmer is not concerned in the least with systems of units and classification of units into systems becomes totally irrelevant (and impossible for many older and rarer units of measure). Therefore, if constraining units into discrete systems is a desired objective, it should be the responsibility of the program rather than the library.
Allowing conversion between units having different dimensions and allowing fundamental units whose dimensions are not fundamental e.g. quarts for volume is very very difficult to implement. Off hand, I think that it requires defining some special system (probably SI) and converting through that system.
Whether the scope should include a particuar system of units (e.g. SI units, Imperial units) is debatable. Personally, I believe each system of units should be treated much the same way C++ currently treats locales. The library may contain, even mandate, a "default" set of units (e.g. SI) but including even the most common modern units is beyond the scope of the library. (Which units are modern? Which units are common?) Each system of units can therefore be defined separately as an extension to the underlying framework.
Absolutely
I'll post more specific review comments about the proposal later.
Thanks, Eric.
Thanks! In Christ, Steven Watanabe

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Steven Watanabe Sent: Wednesday, March 28, 2007 11:05 AM To: boost@lists.boost.org Subject: Re: [boost] [Review] Quantitative Units library review begins today March 26
...
Allowing conversion between units having different dimensions and allowing fundamental units whose dimensions are not fundamental e.g. quarts for volume is very very difficult to implement. Off hand, I think that it requires defining some special system (probably SI) and converting through that system.
Ah. Our first communication breakdown. :) By "system", I'm talking about a set of units regardless of their respective dimensions. For example, foot and ounce are two units of the "Imperial" system of units. Meter and gram are two units of the SI system of units. What you are talking about are compatible units. That is, units of the same dimension whose measured values can be converted from one to the other. Just to clear things up a bit... Eric.

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Steven Watanabe Sent: Wednesday, March 28, 2007 11:05 AM To: boost@lists.boost.org Subject: Re: [boost] [Review] Quantitative Units library review begins today March 26
...
Allowing conversion between units having different dimensions and allowing fundamental units whose dimensions are not fundamental e.g. quarts for volume is very very difficult to implement. Off hand, I think that it requires defining some special system (probably SI) and converting through that system.
It's not that difficult but this does bring up a question that I have. I haven't been able to answer this myself from looking at the proposal so far. How do you implement conversions between units that require more complicated arithmetic expressions (e.g. units of temperature)? Conversions for the vast majority of units are simply multiples of each other and the conversion can be expressed at compile-time and multiplied by a runtime quantity value: template < typename ValueType, typename Numer, typename Denom > ValueType convert (const ValueType& v) { // Numer and Denom are Integral Constant Wrappers. return v * (Numer() / Denom()); } For units of composite dimensions, the runtime quantity value is simply multipled by the conversion factor for each base unit. But a few troublesome units (e.g temperature) can not be expressed so easily and to do so at compile time requires some sort of arithmetic lambda expression that accepts runtime values as arguments for the placeholders. Consider the following _hypothetical_ code: template < typename PlaceholderExpression > struct convert { template < typename ValueType > ValueType apply (const ValueType& v) { // The arity of the placeholder expression should be 1. return apply_runtime< PlaceholderExpression > (v); } }; typedef typename convert < // Hope I got this expression right. :) divide< multiply< subtract<_1, int_<32>>, int_<10>>, int_<18>> >
convert_Farenheit_to_Celsius;
The only alternative that I can think of is to do such conversions completely at runtime. Eric.

How do you implement conversions between units that require more complicated arithmetic expressions (e.g. units of temperature)? Conversions for the vast majority of units are simply multiples of each other and the conversion can be expressed at compile-time and multiplied by a runtime quantity value:
Take a look at unit_example_20 to see a demonstration of implementing temperature conversions (both absolute and relative). It turns out that relative temperature conversions are trivial (converting a temperature difference of 32 degrees Fahrenheit to Kelvin just involves the scale factor of 5/9). Absolute temperatures are the issue. Basically, at this point the library assumes that all quantities are relative as that makes the default behavior the most sensible. While it is probably worthwhile to have the absolute<> concept (also could be point<>) as a part of boost::numeric, I don't think it is really something that properly belongs in the unit library itself; hence it is only an example... You can achieve basically arbitrary conversions between quantities by specializing the conversion_helper<quantity1,quantity2> class... Matthias

AMDG Eric Lemings <lemings <at> roguewave.com> writes:
Allowing conversion between units having different dimensions and allowing fundamental units whose dimensions are not fundamental e.g. quarts for volume is very very difficult to implement. Off hand, I think that it requires defining some special system (probably SI) and converting through that system.
It's not that difficult
How would you do it? Using some special set of units has problems with extensibility. Suppose someone tried to add currency and used US dollars as the base and at the same time someone else used euros as the base. In Christ, Steven Watanabe

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Steven Watanabe Sent: Wednesday, March 28, 2007 12:03 PM To: boost@lists.boost.org Subject: Re: [boost][Review] Quantitative Units library review begins today March 26
AMDG
Eric Lemings <lemings <at> roguewave.com> writes:
Allowing conversion between units having different dimensions and allowing fundamental units whose dimensions are not fundamental e.g. quarts for volume is very very difficult to implement. Off hand, I think that it requires defining some special system (probably SI) and converting through that system.
It's not that difficult
How would you do it? Using some special set of units has problems with extensibility. Suppose someone tried to add currency and used US dollars as the base and at the same time someone else used euros as the base.
Ah sorry. I misread your initial comment. I thought you were referring to units with multiple but same dimensions rather than units with different dimensions. In such cases, it's not just very difficult but is completely impossible according to the rules for dimensional analysis. Eric.

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Steven Watanabe Sent: Wednesday, March 28, 2007 12:03 PM To: boost@lists.boost.org Subject: Re: [boost][Review] Quantitative Units library review begins today March 26
...
How would you do it? Using some special set of units has problems with extensibility. Suppose someone tried to add currency and used US dollars as the base and at the same time someone else used euros as the base.
Speaking of currency however, that is another completely different problem as conversions between units of currency are dynamic rather than fixed like all units of physical measurement. Conversions for such units can only be done at runtime. Eric.

In message <D730FF7CEDDCA64483F9E99D999A158BFCC991@qxvcexch01.ad.quovadx.com>, Eric Lemings <lemings@roguewave.com> writes
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Steven Watanabe Sent: Wednesday, March 28, 2007 12:03 PM To: boost@lists.boost.org Subject: Re: [boost][Review] Quantitative Units library review begins today March 26
...
How would you do it? Using some special set of units has problems with extensibility. Suppose someone tried to add currency and used US dollars as the base and at the same time someone else used euros as the base.
Speaking of currency however, that is another completely different problem as conversions between units of currency are dynamic rather than fixed like all units of physical measurement. Conversions for such units can only be done at runtime.
For current data. But not for historic values. -- Alec Ross

AMDG Eric Lemings <lemings <at> roguewave.com> writes:
Speaking of currency however, that is another completely different problem as conversions between units of currency are dynamic rather than fixed like all units of physical measurement. Conversions for such units can only be done at runtime.
Eric.
As a matter of fact, such conversions work with Units since we don't do anything fancy like caching the total conversion factor. template<> base_unit_converter<currency, yen, euro> { typedef ... type; static void value() { return(lookup_currency_conversion("yen", "euro")); } }; In Christ, Steven Watanabe

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Steven Watanabe Sent: Wednesday, March 28, 2007 1:07 PM To: boost@lists.boost.org Subject: Re: [boost][Review] Quantitative Units library review begins today March 26
...
As a matter of fact, such conversions work with Units since we don't do anything fancy like caching the total conversion factor.
template<> base_unit_converter<currency, yen, euro> {
... Question. Does this base_unit_converter class template imply that there must be a template specicialization for every conversion between all combinations of compatible units? If so, I would have a serious issue with that. Eric.

AMDG Eric Lemings <lemings <at> roguewave.com> writes:
Question. Does this base_unit_converter class template imply that there must be a template specicialization for every conversion between all combinations of compatible units? If so, I would have a serious issue with that.
Answer. Yes it does. If it is too much of a problem you can use partial specialization. struct currency_tag : ordinal<10> {}; //physical units use 1-9 template<int N> struct currency : ordinal<currency_base + N> {}; typedef currency<0> yen; const char* get_currency_name(yen) { return("yen"); } typedef currency<1> us_dollars; ... template<int N1, int N2> struct base_unit_converter<currency_tag, currency<N1>, currency<N2> > { typedef ... type; static type value() { return( lookup_currency_conversion( get_currency_name(currency<N1>()), get_currency_name(currency<N2>()))); } }; In Christ, Steven Watanabe

On 3/28/07, Steven Watanabe <steven@providere-consulting.com> wrote:
Eric Lemings <lemings <at> roguewave.com> writes:
Question. Does this base_unit_converter class template imply that there must be a template specicialization for every conversion between all combinations of compatible units? If so, I would have a serious issue with that.
Answer. Yes it does. If it is too much of a problem you can use partial specialization.
Steven, do you think it would be easy enough to provide a reciprocal helper for when conversions are that straightforward (I'm not familiar enough in this domain to know how many instances there are where the conversion is not)? E.g. currently you have to do: template<> struct base_unit_converter<length_tag,nautical::system_tag,SI::system_tag> { typedef double type; static type value() { return 1.852e3; } }; template<> struct base_unit_converter<length_tag,SI::system_tag,nautical::system_tag> { typedef double type; static type value() { return 1.0/1.852e3; } }; Just defining the first one should be enough, and the second one should implicitly be the reciprocal of the first (but still give the user the option to explicitly say otherwise, of course). --Michael Fawcett

AMDG Michael Fawcett <michael.fawcett <at> gmail.com> writes:
Steven, do you think it would be easy enough to provide a reciprocal helper for when conversions are that straightforward (I'm not familiar enough in this domain to know how many instances there are where the conversion is not)?
E.g. currently you have to do:
template<> struct base_unit_converter<length_tag,nautical::system_tag,SI::system_tag> { typedef double type; static type value() { return 1.852e3; } };
template<> struct base_unit_converter<length_tag,SI::system_tag,nautical::system_tag> { typedef double type; static type value() { return 1.0/1.852e3; } };
Just defining the first one should be enough, and the second one should implicitly be the reciprocal of the first (but still give the user the option to explicitly say otherwise, of course).
We just make the primary template: struct define_reverse_automatially {}; struct undefined_conversion {}; template<class Converter> struct reverse_conversion { typedef typename Converter::type type; static type value() { return(1/Converter::value()); } }; template<class Dimension, class Tag1, class Tag2> struct base_unit_converter : mpl::eval_if<is_same<Tag1, Tag2>, mpl::identity<trivial_conversion>, mpl::if_< is_base_and_derived< define_reverse_automatially, base_unit_converter<Dimension,Tag2,Tag1> >, reverse_conversion<base_unit_converter<Dimension,Tag2,Tag1> >, undefined_conversion >
::type {};
The you can write template<> struct base_unit_converter<length_tag,nautical::system_tag,SI::system_tag> : define_reverse_automatically { typedef double type; static type value() { return 1.852e3; } }; In Christ, Steven Watanabe

Steven Watanabe wrote:
How would you do it? Using some special set of units has problems with extensibility. Suppose someone tried to add currency and used US dollars as the base and at the same time someone else used euros as the base.
Currency is even worse than that. Currency exchange rates fluctuate at least daily so the conversion must be table driven so that it can be updated. Since this can't be just a compile time conversion it really doesn't fit well with the rest of the library. In fact its probably more correct to avoid thinking of currencies as a fundamental units since both their absolute value (i.e. purchasing power) and relative values don't have stable real-world meanings. OTOH, if everybody changed to a gold based standard then things might be different but there is probably a legion of economists yelling "no way in hell" at their screen right about now... -glenn

AMDG Glenn Schrader <gschrad <at> ll.mit.edu> writes:
Currency is even worse than that. Currency exchange rates fluctuate at least daily so the conversion must be table driven so that it can be updated. Since this can't be just a compile time conversion it really doesn't fit well with the rest of the library.
Nothing in the library mandates that the conversion factor be constant. In Christ, Steven Watanabe

Input is definitely beyond the scope of the library. We have implemented primitive output.... On further thought, we should probably just have an output operator for quantity that prints the value_type then the unit and leave unit output alone.
Again, input and output are fully supported through Boost.Serialization - just not in a format designed for human consumption. I think we need to provide at least a rudimentary system for unit/quantity output for debugging purposes is nothing else. Steven suggested the possibility of boost::lexical_cast, which seems reasonable. Matthias

Steven Watanabe wrote:
The scope should not include any facilities for input and output. It should also not prevent such support however. (Input and output of units and measures can be handled by extending C++ locales with new facets. C++ locales already deal with I/O of time and money. More about internalization and localization below.)
Input is definitely beyond the scope of the library. We have implemented primitive output.... On further thought, we should probably just have an output operator for quantity that prints the value_type then the unit and leave unit output alone.
That sounds sensible: I can imagine wanting to just log values and having a default << operator is a big help. Users can provide more specialised << operators for specific units if that what they want. Just my rather uninformed 2c.... Regards, John.
participants (7)
-
Alec Ross
-
Eric Lemings
-
Glenn Schrader
-
John Maddock
-
Matthias Schabel
-
Michael Fawcett
-
Steven Watanabe