Any interest in a decimal and money class?

(I know that "Currency" received 0 votes at the OOPSALA conference but maybe someone is interested anyway) I have implemented a type for handling decimal math and a type specialized for monetary values. decimal64 type ------------- The type stores a decimal value with specified numberof decimals exactly and also performs all arithmetic operations (+,-,*,/) using decimal math. (Internally the type stores the value as an int64 value which gives 18 DECIMAL digits precision. Multiply and divide uses a 128 bit temporay to avoid rounding errors) - All std rounding modes are available plus a few extra such as round_to_even. - Construction of a decimal64 can be made from int, double, string literal (e.g. "1.234") and other decimal64 values. - Operations can be made on decimal64 objects with different number of decimals and/or different rounding policy. Result of a binary operation on two decimal64 values will have the number of decimals and rounding policy of the left operand. - Streaming works the same way as floating point values but the scientific format is ignored. There are 2 decimal64 types defined: - decimal64 - all constructors requires specification of number of decimals and optionally rounding mode. decimal64(3.1415, 4, round_to_nearest); - decimal64_spec - a template version of decimal64 where number of decimals and rounding mode are specified as template parameters which allows a default constructor. typedef decimal64_spec<3, round_to_even> decimal3; decimal3 x(3.14), y; The decimal value can be converted into another at any time if a calculation needs to be performed at a higher precision or with another rounding policy. decimal64 x(1.2, 1, round_to_nearest); // calculate x*PI/2 using 5 decimals // result is stored in x with 1 decimal, // rounded with round_to_nearest policy x = x.as_decimal(5) * decimal64(3.14159, 5) / 2; Some things I would specificly like to have comments on: 1. Operations are not allowed on doubles. Double values always need to be converted into a decimal value first. e.g. x * decimal64(3.14, 2). Is this something that is needed? 2. No overflow detection or +inf etc. Just like with the integer types there is no checking for overflow on operations. Money type ----------- The money type uses a decimal type to store a monetary value. The functionality is the same as for the decimal64 type with the following exception: 1. Operations are more limited to detect errors. The limitations are: - Money * Money is not allowed. (Money * Decimal is allowed) - Money / Money is allowed but the result is a Decimal (not money) - Money + Decimal is not allowed (Money + Money is allowed). (The same applies to - and all comparison operators) Integers works as both money and decimal so Money*2 and Money+1 is always allowed. 2. Streaming uses locale moneypunct for formatting and parsing. As with the decimal type two money classes are defined - money_base - construction always requires number of decimals and optionally rounding policy typedef money_base<decimal64> money64; money64 x(100,2,round_toward_infinity); - money_spec - number of decimals and rounding policy are template parameters. typedef money_spec<decimal64, 4, round_toward_neg_infinity> money4; money4 x("9.99"), y; locale library -------------- Some time ago implemented helper classes for locale but I never got around submitting it to boost. I needed it for testing the decimal64/money streaming so I cleaned it up and it is included in the package. Building -------- A jam file for building decimal library is included. I have compiled all files and testcases with VS2002 (VC70) and "g++ (GCC) 3.4.2 (mingw-special)". Couldn't get the boost test jam file to work but command line g++ works fine. g++ libs/decimal/test/decimal64_test.cpp libs/decimal/src/decimal64.cpp g++ libs/decimal/test/money_test.cpp libs/decimal/src/decimal64.cpp libs/decimal/src/money.cpp g++ libs/locale/test/locale_test.cpp libs/decimal/src/decimal64.cpp libs/decimal/src/money.cpp There are some differences in streaming which causes a few test errors for money: - VC70 only inserts spaces in the money format if the "internal" adjustment flag and width is set. g++ always inserts 1 space when specifed in the format. I assumed g++ was right so VC70 fails this test. - The default locale with g++ seem to miss a '-' sign for the money format so it fails tests with negative numbers. Library is available here http://web.telia.com/~u84606172/Boost/ Documentation is available in the download as well.

Martin wrote:
(I know that "Currency" received 0 votes at the OOPSALA conference but maybe someone is interested anyway)
I have implemented a type for handling decimal math and a type specialized for monetary values.
I strongly suggest to use a currency, as otherwise you'll likely miss the intended audience's requirements. If you are handling monetary values, you want as much safety as possible. This means that you must make sure you don't add EUR and USD! Our company's implementation of the currency class uses a 32-bit integer internally, which holds 4 characters: 3 represent the currency, the 4th is \0. Also, the 0 itself is allowed (as the empty currency). Makes the handling of currencies very efficient, still the interface seems to use strings/char*'s. Ugly casting internall, though, but at least it's encapsulated :) FWIW, I'll aks my boss on monday if it's OK to take some of the code here if there is interest in such a library. One thing which I invented during the implementation was already shown in clc++m: <http://tinyurl.com/5l7tb> (links to Google archieve). After some months of experience with it, I'm quiet satified with it. It makes using the money class really easy: typedef std::vector< money > V; money balance( const V& v ) { money s = 0; // Note: no currency! for( V::const_iterator it = v.begin(); it != v.end(); ++it ) { s += *it; } return s; } bool is_debtor( const V& v ) { return balance( v ) < 0; // Note: no currency! } This throws an exception if the vector contains different currencies, otherwise it works pretty well :) Regards, Daniel
Some things I would specificly like to have comments on:
1. Operations are not allowed on doubles. Double values always need to be converted into a decimal value first. e.g. x * decimal64(3.14, 2). Is this something that is needed?
In the banking area, it's important to allow using types with a base-10-representation. Rounding errors due to binary stored numbers are often a problem. Consider a typical bug: double d = 0; for( int i=0; i<100; ++i ) d += .01; if(d==1) { ... } Conclusion: Make it damn hard for users to use inexact values. Our solution is (like yours): explicit conversion functions or use strings.
2. No overflow detection or +inf etc. Just like with the integer types there is no checking for overflow on operations.
Reasonable, IMHO.
Integers works as both money and decimal so Money*2 and Money+1 is always allowed.
money*int is OK, money+int is not. And FWIW, I never saw any of my colleagues complain about money+int not compiling. If the compiler happens to catch it, it usually means that it saved them from a having bug!
typedef money_base<decimal64> money64;
I suggest the following classes/names which I used when designing our company's decimal/money/currency-framework: currency basic_decimal<T> (interface for some T, e.g. decimal64 from above, long double, ...) basic_money<T> (consists of a basic_decimal<T> and a currency) The application can/should then create central typedefs: typedef basic_decimal<long double> decimal; typedef basic_money<decimal::value_type> money; That way, you can switch your complete application for different underlying types that are used for the real calculations easily. Regards, Daniel

Daniel Frey <d.frey <at> gmx.de> writes:
I strongly suggest to use a currency, as otherwise you'll likely miss the intended audience's requirements.
I actually had this implemented at start but removed it at one point. I implemented it like this: template <class DecimalT, int CurrencyId = 0> class money_base ... You created different currencies by doing typedef money_base<decimal64, 1> USD; typedef money_base<decimal64, 2> EUR; If I needed values without currency I just used the decimal type. decimal exchangerate(usd.as_decimal(5) / eur.as_decimal(), 5); // 5 decimals (Your debtor example would probably need to use boost.any with my solution) Your run-time currency type is an interesting approach.
Integers works as both money and decimal so Money*2 and Money+1 is always allowed.
money*int is OK, money+int is not. And FWIW, I never saw any of my colleagues complain about money+int not compiling. If the compiler happens to catch it, it usually means that it saved them from a having bug!
And I was afraid my interface was to strict. Thanks.
The application can/should then create central typedefs:
typedef basic_decimal<long double> decimal; typedef basic_money<decimal::value_type> money;
Not sure I understand. The implementation of decimal arithmetic will be very different depending on the type (e.g. double, int, BCD strings) so I don't see how it can all fit into a basic_decimal. It is fine as an ABC but does it really add anything (except virtual calls). Also shouldn't basic_money use the decimal type instead of the decimal type's internal representation (i.e. basic_money<decimal>)
That way, you can switch your complete application for different underlying types that are used for the real calculations easily.
You can do the same with my solution but it would look like this: typedef money_base<decimal64> money; or typedef money_base<decimalBCD> money; typedef money::decimal_type decimal;

Martin wrote:
Daniel Frey <d.frey <at> gmx.de> writes:
I strongly suggest to use a currency, as otherwise you'll likely miss the intended audience's requirements.
I actually had this implemented at start but removed it at one point. I implemented it like this:
template <class DecimalT, int CurrencyId = 0> class money_base ...
In our applications, we can't predict the currency of a money variable at compile-time, and the overhead of the currencies is acceptable due to the very lean implementation (only 4 bytes, single comparison for checks).
money*int is OK, money+int is not. And FWIW, I never saw any of my colleagues complain about money+int not compiling. If the compiler happens to catch it, it usually means that it saved them from a having bug!
And I was afraid my interface was to strict. Thanks.
Safety over convenience. I think most people that actually *work* with such stuff prefer it that way ;)
The application can/should then create central typedefs:
typedef basic_decimal<long double> decimal; typedef basic_money<decimal::value_type> money;
Not sure I understand. The implementation of decimal arithmetic will be very different depending on the type (e.g. double, int, BCD strings) so I don't see how it can all fit into a basic_decimal. It is fine as an ABC but does it really add anything (except virtual calls).
Also shouldn't basic_money use the decimal type instead of the decimal type's internal representation (i.e. basic_money<decimal>)
The basic_decimal<T>-class is just a generic float point interface. It defines what can be done, not how it's done. Example: Depending on T, some classes have member functions where others have free functions. It's round(x,2) vs. x.round(2). Combine that with different rounding modes. When you write your application for one of these types directly, you are tied to it. Later, when you need to switch the float point type, you have to change a lot of code. basic_decimal is meant to solve this. It forwards most calls to the implementations for a type T, unifying the syntax. Actually, I have a decimal_traits<T>-class which is specialized for all T's that can be used in our applications with basic_decimal and basic_money. This is also the reason, why I use basic_money<T> instead of basic_money<basic_decimal<T>>. This is safer *and* easier for the user, as it's shorter and basic_money<basic_decimal<T>> can be caught by the compiler. You can't do that otherwise. While we are at it: It seems to me that your decimal64 is just a candidate for a T for basic_decimal<T>. Maybe we should separate both discussions, as my basic_decimal<T> is not meant to be a cool float point type in itself.
That way, you can switch your complete application for different underlying types that are used for the real calculations easily.
You can do the same with my solution but it would look like this:
typedef money_base<decimal64> money; or typedef money_base<decimalBCD> money;
typedef money::decimal_type decimal;
Only if decimal64 and decimalBCD have the same interface. And you can't replace it with double or RWDecimal<RWMP3Int>, because their interfaces differ. Regards, Daniel

In our applications, we can't predict the currency of a money variable at compile-time, and the overhead of the currencies is acceptable due to the very lean implementation (only 4 bytes, single comparison for checks).
Yes, that was one of the reasons I removed the currencyid from the type but I didn't find any good way to specify the currency for individual objects. Do you have it as a constructor argument or is it a property?
The basic_decimal<T>-class is just a generic float point interface. It defines what can be done, not how it's done. Example: Depending on T, some classes have member functions where others have free functions. It's round(x,2) vs. x.round(2).
Sorry but I don't understand what you gain from that approach. If I understand correctly you will need a specialization of basic_decimal for each type T. A specialization has no correlation to the template (except the name) so you will not get any compile errors if the specialization basic_decimal<double> has a completly different interface than basic_decimal<int>.
syntax. Actually, I have a decimal_traits<T>-class which is specialized for all T's that can be used in our applications with basic_decimal and basic_money.
Ok, basic_decimal only forwards calls to the decimal_traits but that still doesn't change the fact that you need specializations for each type. Why is it better to have decimal_traits<decimal64> & decimal_traits<decimalBCD> than to implement decimal64 & decimalBCD to have the same interface? I can understand it if there are some types that don't need specialization of decimal_traits. Writing basic_money<double> is very confusing to me since it gives me no hint on what I get. If I write basic_money<decimalDouble> I understand that I can look in the documentation for deicmalDouble.
Only if decimal64 and decimalBCD have the same interface. And you can't replace it with double or RWDecimal<RWMP3Int>, because their interfaces differ.
In your implementation decimal_traits<decimal64> and decimal_traits<decimalBCD> need to have the same interface so what's the difference. Don't get me wrong here. I am usually in favour of traits classes but in this case I don't see the point. Going forward. Can we combine our work? I like your run-time currency and maybe you can convince me about the traits but I assume your solution is part of your work so it can't be used in boost.

Martin wrote:
In our applications, we can't predict the currency of a money variable at compile-time, and the overhead of the currencies is acceptable due to the very lean implementation (only 4 bytes, single comparison for checks).
Yes, that was one of the reasons I removed the currencyid from the type but I didn't find any good way to specify the currency for individual objects.
Do you have it as a constructor argument or is it a property?
ctor. Either directly: money m( 42, "EUR" ); or indirectly through a predefined currency to prevent typos and make it even more efficient: const currency EUR( "EUR" ) // Only once in some global project header money m( 42, EUR ); additionally, you can use: decimal d = m.amount(); // getter m.amount( d ); // setter currency c = m.currency(); // getter m.currency( c ); // setter to access the parts.
In your implementation decimal_traits<decimal64> and decimal_traits<decimalBCD> need to have the same interface so what's the difference.
Don't get me wrong here. I am usually in favour of traits classes but in this case I don't see the point.
The point is that when I want to use a new underlying type for calculations, I have a single point of customization, about 100-200 lines. The specialization of decimal_traits<T> for that type T and the typedef for decimal. I don't have to touch the 300.000+ lines of code that use it. What do you prefer?
Can we combine our work? I like your run-time currency and maybe you can convince me about the traits but I assume your solution is part of your work so it can't be used in boost.
The folks in my company are basically in favor of boost, so I hope they allow me to take some of the stuff here. Even if they don't allow it, I can still take my knowledge of general programming techniques (where lots of them come from boost!) and implement the classes from scratch. It'll just take a bit longer. To combine our work, I think it's best to separate it. As I said, decimal64 might be a candidate for basic_decimal<T>'s T. As I write this, I realize that I can describe basic_decimal as a helper for basic_money<T> (and the application as a whole) to decouple from T's interface. Maybe this helps to understand why basic_decimal is there. I'll ask about using the company's basic_decimal/basic_money/currency as a template for the boost implementation tomorrow and we can then discuss it independently of decimal64. I think it's most useful if you work on decimal64 independently of money/currency issues. Make it a cool new float-point type and basic_decimal will be able to use it :) Another benefit of the indirection through basic_decimal<T>: FWIW, there is one thing that my classes don't do right now and which I always dreamed of: "Correct" epsilon handling. Like operator== respects a given epsilon native_less(a,b) like the current operator< for the underlying type a!=b -> !(a==b) a<b -> native_less(a,b) && a!=b a>b -> native_less(b,a) && a!=b a<=b -> native_less(a,b) || a==b a>=b -> native_less(b,a) || a==b But we could encapsulate that in the decimal_traits<T>, too, so you can switch your application from no epsilon to different epsilons in a single place. Wouldn't that be cool? :o) Regards, Daniel

The point is that when I want to use a new underlying type for calculations, I have a single point of customization, about 100-200 lines. The specialization of decimal_traits<T> for that type T and the typedef for decimal. I don't have to touch the 300.000+ lines of code that use it. What do you prefer?
With your solution: 1. typedef basic_decimal<decimal64> decimal 2. typedef basic_decimal<decimalBCD> decimal With my solution 1. typedef decimal64 decimal; 2. typedef decimalBCD decimal; Same amount of code to change. (We both need to implement the interface, you do it in decimal_traits, I do it in decimal64 & decimalBCD). What worries me with your solution is that your decimal_traits class implies a way to implement things that might not be the most efficient one for each implementation. Lets take an example: Assume that basic_decimal implements "<=" as "traits::less_than || traits::equal" which works fine if "less_than" and "equal" are "cheap" operations. In decimal64, both "less_than" & "equal" might need to scale one of the operands before the comparison so the scaling occurs twice in "<=". With my solution I am free to implement the interface in the most efficient way for each class.
I think it's most useful if you work on decimal64 independently of money/currency issues. Make it a cool new float-point type and basic_decimal will be able to use it :)
Do you have an example of an application where decimal floating point is useful? To me floating point means that you give up precision to be able to handle a large range of values. But if loss of precision is acceptable, you can just as well use binary floating point.
Another benefit of the indirection through basic_decimal<T>: FWIW, there is one thing that my classes don't do right now and which I always dreamed of: "Correct" epsilon handling. Like
operator== respects a given epsilon native_less(a,b) like the current operator< for the underlying type
Isn't the whole point with decimal math that values are stored exactly? Why would you need epsilon for exact values?

Martin wrote:
The point is that when I want to use a new underlying type for calculations, I have a single point of customization, about 100-200 lines. The specialization of decimal_traits<T> for that type T and the typedef for decimal. I don't have to touch the 300.000+ lines of code that use it. What do you prefer?
With your solution: 1. typedef basic_decimal<decimal64> decimal 2. typedef basic_decimal<decimalBCD> decimal
With my solution 1. typedef decimal64 decimal; 2. typedef decimalBCD decimal;
No. I mean, yes, this works for two types, when both share the same interface. But how can you be sure that RWDecimal<RWMP3Int> is compatible with your decimal64? typedef RWDecimal<RWMP3Int> decimal; is unlikely to work for your project.
Same amount of code to change. (We both need to implement the interface, you do it in decimal_traits, I do it in decimal64 & decimalBCD). What worries me with your solution is that your decimal_traits class implies a way to implement things that might not be the most efficient one for each implementation.
Your decimal64 is both an interface *and* an implementation. I just suggest to separate it. Let basic_decimal take care of the interface, while decimal64 takes care of the implementation.
Lets take an example: Assume that basic_decimal implements "<=" as "traits::less_than || traits::equal" which works fine if "less_than" and "equal" are "cheap" operations. In decimal64, both "less_than" & "equal" might need to scale one of the operands before the comparison so the scaling occurs twice in "<=".
That won't be a problem, as the decimal_traits class will implement the operators. By default, every call to an operator should be forwarded (inline) to the underlying type. No inefficiency involved, only freedom for the user to overwrite operators when he wishes to.
With my solution I am free to implement the interface in the most efficient way for each class.
Me too.
I think it's most useful if you work on decimal64 independently of money/currency issues. Make it a cool new float-point type and basic_decimal will be able to use it :)
Do you have an example of an application where decimal floating point is useful? To me floating point means that you give up precision to be able to handle a large range of values. But if loss of precision is acceptable, you can just as well use binary floating point.
Maybe I don't understand the question, but I think it should be obvious that a decimal based float point types has a lot of advantages. It's not free of any rounding issues, but the problems are more intuitive. Most people don't have a problem to accept rounding in general, it's just examples like double d = 0; for( int i=0; i<100; ++i ) d += .01; if( d == 1 ) { ... } that are a source of so many problems.
Another benefit of the indirection through basic_decimal<T>: FWIW, there is one thing that my classes don't do right now and which I always dreamed of: "Correct" epsilon handling. Like
operator== respects a given epsilon native_less(a,b) like the current operator< for the underlying type
Isn't the whole point with decimal math that values are stored exactly? Why would you need epsilon for exact values?
If you really try to store the values exactly, consider: decimal64 d = 1; d /= 3; d *= 3; if( d == 1 ) { ... } will this work for your type? How? Regards, Daniel

Your decimal64 is both an interface *and* an implementation. I just suggest to separate it. Let basic_decimal take care of the interface, while decimal64 takes care of the implementation.
I can agree that the interface could be formally specified as a basic_decimal but still don't understand the benefits of a decimal_traits vs a class that implements the basic_decimal interface. There are several string types which implements the basic_string interface (fixed_string, const_string, flex_string etc) and they all work without string_traits. Since I am familiar with the string type maybe you can explain what the benefits would be if all string types were implemented using string_traits instead. One thing that would be nice is if basic_decimal were an ABC and all decimal types were derived from it. That way a function could work on any decimal type without templates or recompiling. Drawback is that it would introduce a virtual call for each operation but since a decimal type is probably slow anyway I don't think it matters.
Maybe I don't understand the question, but I think it should be obvious that a decimal based float point types has a lot of advantages. It's not free of any rounding issues, but the problems are more intuitive. Most people don't have a problem to accept rounding in general, it's just examples like
double d = 0; for( int i=0; i<100; ++i ) d += .01; if( d == 1 ) { ... }
Floating point is difficult to use and you must be aware of its weaknesess. Decimal floating point solves the addition problem above but as soon as you do a division the problems are the same as with binary floating point. Expressions often involves several operations: x = (a + b /c - d)*e; A test like (x == 1) will probaly fail for both a decimal and binary floating point type.
If you really try to store the values exactly, consider:
decimal64 d = 1; d /= 3; d *= 3; if( d == 1 ) { ... }
will this work for your type? How?
It will not work since decimal64 is a fixed precision decimal type and will only store a fixed number of decimals. The above will only work for a rational type. With exact I mean that if the decimal type got 2 decimals then these are stored exactly. 1/3 becomes exactly 0.33. How do you expect epsilon to work here? Should == return true for 0.32 0.33 0.34 or an even wider range? Sound confusing to me.

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Martin Sent: Sunday, January 09, 2005 1:40 PM To: boost@lists.boost.org Subject: [boost] Re: Any interest in a decimal and money class?
One thing that would be nice is if basic_decimal were an ABC and all decimal types were derived from it. That way a function could work on any decimal type without templates or recompiling. Drawback is that it would introduce a virtual call for each operation but since a decimal type is probably slow anyway I don't think it matters.
I couldn't disagree more. The IEEE 754r spec is about decimal floating-point hardware. Any ABC, with its attendant virtual function calls, is certainly a non-starter. A good starting point to 754r is here: http://en.wikipedia.org/wiki/IEEE_754r -- Noah

At 03:17 PM 1/9/2005, Noah Stein wrote:
... The IEEE 754r spec is about decimal floating-point hardware.
Note that fixed-point and integer can also be represented. More details available here: http://www2.hursley.ibm.com/decimal/decbits.pdf --Beman

From: Martin <adrianm@touchdown.se>
Your decimal64 is both an interface *and* an implementation. I just suggest to separate it. Let basic_decimal take care of the interface, while decimal64 takes care of the implementation.
I can agree that the interface could be formally specified as a basic_decimal but still don't understand the benefits of a decimal_traits vs a class that implements the basic_decimal interface.
basic_decimal can utilize fundamental operations from the traits class to provide the full interface. Put another way, the traits class needn't provide the entire interface that basic_decimal provides, so adapting a new type to fulfill the basic_decimal interface is easier. (That's theoretical, of course. I haven't any idea how much logic Daniel's basic_decimal implements beyond that provided by his traits class.) -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Rob Stewart wrote:
From: Martin <adrianm@touchdown.se>
Your decimal64 is both an interface *and* an implementation. I just suggest to separate it. Let basic_decimal take care of the interface, while decimal64 takes care of the implementation.
I can agree that the interface could be formally specified as a basic_decimal but still don't understand the benefits of a decimal_traits vs a class that implements the basic_decimal interface.
OK, ask Roguewave to modify their classes to implement the basic_decimal interface. They won't do that, right? So what will you do now? Write a wrapper? OK, but what is the easiest way to write a wrapper? I don't want to limit the T's to types that conform to my interface. So I need wrappers. So, IMHO, the question is how to make it safe and easy to create such wrappers (or adaptors)?
basic_decimal can utilize fundamental operations from the traits class to provide the full interface. Put another way, the traits class needn't provide the entire interface that basic_decimal provides, so adapting a new type to fulfill the basic_decimal interface is easier. (That's theoretical, of course. I haven't any idea how much logic Daniel's basic_decimal implements beyond that provided by his traits class.)
I think this is generally correct, although there were more reasons for a basic_decimal class. It allows me to control the interactions with basic_money. I can declare operators like this: template< typename T > basic_money<T> operator*(basic_money<T> lhs, basic_decimal<T> rhs) which is better than allowing basic_money<T> * T because I know more about basic_decimal<T> than about T. Maybe this is influenced by the bad design of some T's, but that's the real world. :( So this argument for basic_decimal<T> might also be seen as nannyism, but to me, basic_money and basic_decimal belong together because it's the only way I can make basic_money safe. having a decimal_traits class and writing mappings to some type T is a consequence. And having written basic_decimal<T> myself, I know exactly all ctors, conversions, etc. provided, no chance for T to create a SNAFU but adding a ctor which takes a double or something stupid like that. The decimal_traits are not only a helper to write a mapper to some T faster, they also help to make it safer, because their interface is more explicit and more restricted than that of a generally useful interface of some T. Regards, Daniel

Daniel Frey wrote:
The folks in my company are basically in favor of boost, so I hope they allow me to take some of the stuff here. Even if they don't allow it, I can still take my knowledge of general programming techniques (where lots of them come from boost!) and implement the classes from scratch. It'll just take a bit longer.
For the records: I was allowed to take the company's code here. I'm currently refactoring it to make it more readable, etc. and to adapt it to the Boost style, naming conventions, etc. (and the Boost license :). That might take some time, but I'll post something in the next days. Don't expect a complete solution, though. I can just provide a framework, some important details will be missing as they are not available in Boost currently. As an example, we write a stack trace to our logging system when an exception is thrown. Boost has currently no means to do that, so I can just add a comment instead. Still, the handling of currencies should be a good starting point for others. Regards, Daniel

At 09:36 AM 1/8/2005, Martin wrote:
(I know that "Currency" received 0 votes at the OOPSALA conference but maybe someone is interested anyway)
I have implemented a type for handling decimal math ...
How does your decimal library compare to Bill Seymore's library that we reviewed some time ago? With the C and C++ committee's working on a decimal arithmetic technical report, have you considered how your library will support future decimal types that come out of that effort? Since at least one major vendor (IBM) apparently intends to support the proposed decimal types in hardware, the new types may be a lot faster on some platforms than decimal types which do not have hardware support. --Beman

Beman Dawes <bdawes <at> acm.org> writes:
How does your decimal library compare to Bill Seymore's library that we reviewed some time ago? "Some time ago" is 3 years since the last post here so I assumed the project was dead. But how much difference can it be? Decimal math either uses fixed precision or floating point and then rounding can either be tied to type or operation. I choosed fixed precision and rounding based on type. If I understood correctly Bill Seymore used fixed precision and rounding on operation.
The rest is just about interface and there I'm open to suggestions on what should be member functions and what should be free functions. Currently my interface is rather bloated and everything is member functions.
With the C and C++ committee's working on a decimal arithmetic technical report, have you considered how your library will support future decimal types that come out of that effort?
Depends on what you mean. My decimal type will probably be obsolete when these new types arrive but the money type shold be able to use them with a wrapper for construction and extraction. Also, I have tried to look at the committe's work but don't really understand how rounding will work with these new types.

On Sat, 8 Jan 2005 20:43:27 +0000 (UTC), Martin wrote
How does your decimal library compare to Bill Seymore's library that we reviewed some time ago? "Some time ago" is 3 years since the last post here so I assumed the
Beman Dawes <bdawes <at> acm.org> writes: project was dead.
Yep, I think that's safe to assume.
But how much difference can it be? Decimal math either uses fixed precision or floating point and then rounding can either be tied to type or operation. I choosed fixed precision and rounding based on type. If I understood correctly Bill Seymore used fixed precision and rounding on operation.
Well, you might want to scour the archives and see if you can see what led to the downfall of Bill's library during the review (I seem to recall there were several things, but those memories are way off the stack by now). Obviously you'll want to make sure you have that ground covered if you are serious about getting this into boost.
With the C and C++ committee's working on a decimal arithmetic technical report, have you considered how your library will support future decimal types that come out of that effort?
Depends on what you mean. My decimal type will probably be obsolete when these new types arrive but the money type shold be able to use them with a wrapper for construction and extraction.
I'm not so sure about that. From I've read about the committee's work it's been mostly at the C level and targeted a decimal types in general. I don't think it addresses the fixed precision / rounding capabilities of your type -- but it's possible I missed something.
Also, I have tried to look at the committe's work but don't really understand how rounding will work with these new types.
I haven't seen anything on that either... Jeff

At 03:43 PM 1/8/2005, Martin wrote:
With the C and C++ committee's working on a decimal arithmetic technical report, have you considered how your library will support future decimal types that come out of that effort?
Depends on what you mean. My decimal type will probably be obsolete when these new types arrive but the money type shold be able to use them with a wrapper for construction and extraction.
What I had in mind was something along the lines of Daniel Frey's basic_decimal<T> suggestion that might be able to adapt to future T decimal types. Just a thought.
Also, I have tried to look at the committe's work but don't really understand how rounding will work with these new types.
IIUC, details will be based on the new IEEE 754R standard, so you might want to look at that. --Beman

Martin wrote:
How does your decimal library compare to Bill Seymore's library that we reviewed some time ago? "Some time ago" is 3 years since the last post here so I assumed the
Beman Dawes <bdawes <at> acm.org> writes: project was dead.
The review seems to have been in July 2003. See, e.g., http://lists.boost.org/MailArchives/boost/msg49768.php Jonathan

Beman Dawes wrote: [...]
With the C and C++ committee's working on a decimal arithmetic technical report, have you considered how your library will support future decimal types that come out of that effort? Since at least one major vendor (IBM) apparently intends to support the proposed decimal types in hardware, the new types may be a lot faster on some platforms than decimal types which do not have hardware support.
Bewman, wasn't it you who recently asked for clarification about the IBM licencing for decimal? The one and only currently available implementation of the proposed decimal interface is a library that you can use for about $3000. This totally conflits with everything what I've ever heard about standardization, as it's IMHO very difficult to write anything that meets the same interface requirements and doesn't conflict with the current implementation! I myself am not a mathematician, thus I've never been able to seriously propose anything for a very important datatype that I myself use very frequently (I wrote my own decimal implementation some time ago). However, I'm really angry about the dog and pony show to adopt a datatype that goes without saying in other programming languagues. Stefan

At 06:54 PM 1/8/2005, Stefan Slapeta wrote:
Beman Dawes wrote:
[...]
With the C and C++ committee's working on a decimal arithmetic technical report, have you considered how your library will support future decimal types that come out of that effort? Since at least one major vendor (IBM) apparently intends to support the proposed decimal types in hardware, the new types may be a lot faster on some platforms than decimal types which do not have hardware support.
Beman,
wasn't it you who recently asked for clarification about the IBM licencing for decimal? The one and only currently available implementation of the proposed decimal interface is a library that you can use for about $3000. This totally conflits with everything what I've ever heard about standardization, as it's IMHO very difficult to write anything that meets the same interface requirements and doesn't conflict with the current implementation!
I asked on one of the committee reflectors, and the response was that the library IBM licenses isn't what is being standardized, so there is no issue. For C, what will be standardized are built-in data types, not a library. For C++, what will be standardized may be a library, but it will be designed for C++.
... However, I'm really angry about the dog and pony show to adopt a datatype that goes without saying in other programming languagues.
Sorry, I'm having trouble understanding your meaning. Are you saying in effect that decimal types should have be standardized in C and C++ a long time ago? --Beman

Beman Dawes wrote:
I asked on one of the committee reflectors, and the response was that the library IBM licenses isn't what is being standardized, so there is no issue. For C, what will be standardized are built-in data types, not a library. For C++, what will be standardized may be a library, but it will be designed for C++.
From a user's perspective, yes. Internally, the solution will be based on the same C interface to enable any hardware support for the future, no?
Sorry, I'm having trouble understanding your meaning. Are you saying in effect that decimal types should have be standardized in C and C++ a long time ago?
Yes, today there are some proposals for extensions that I bet are useful for _much_ less people than a decimal data type would be. br, Stefan

At 04:50 PM 1/9/2005, Stefan Slapeta wrote:
Beman Dawes wrote:
I asked on one of the committee reflectors, and the response was that the library IBM licenses isn't what is being standardized, so there is no issue. For C, what will be standardized are built-in data types, not
a library. For C++, what will be standardized may be a library, but it will be designed for C++.
From a user's perspective, yes. Internally, the solution will be based on the same C interface to enable any hardware support for the future, no?
It isn't clear yet. It is possible that C will treat the three decimal types as built-ins, while C++ will provide a library solution. That is probably unlikely, but at this early stage it is still possible. Bill Plauger is the point man for both C and C++ committees, and his thinking is given in: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1567.htm Note that the operations available are not specified as a C interface, but rather in a metalanguage. See: http://www2.hursley.ibm.com/decimal/decarith.pdf That document also explains the rounding modes available, exceptions, etc.
Sorry, I'm having trouble understanding your meaning. Are you saying in
effect that decimal types should have be standardized in C and C++ a long time ago?
Yes, today there are some proposals for extensions that I bet are useful for _much_ less people than a decimal data type would be.
Both C and C++ committees are committed to decimal arithmetic technical reports, so even though the details are as yet unclear, it is probable that the two languages will get decimal arithmetic. A case of better late than never, I suppose. --Beman

It isn't clear yet. It is possible that C will treat the three decimal types as built-ins, while C++ will provide a library solution. That is probably unlikely, but at this early stage it is still possible.
2 reasons why I would like to see it as a library in c++ (probaly based on some underlying __decimal64 type) 1. The rounding policy is not part of the decimal object so it must be some kind of global setting. To avoid having the same issues as with setlocale (i.e. not thread safe) a library solution could at least handle the locking. 2. The fixed precison is part of the object but how do you specify it. In C I assume the solution would be something like decimal64 f = decimal_fixed_precision(5); // f now got 5 fixed decimals or decimal64 f = 1.2DF5; // last digit specifies number of fixed decimals

On Sat, 8 Jan 2005 14:36:48 +0000 (UTC), Martin wrote
(I know that "Currency" received 0 votes at the OOPSALA conference but maybe someone is interested anyway)
I was at OOPSLA and I think everyone needs to keep in mind that not having a vote at OOPSLA does not imply there is no interest in a library or category. Obviously we had a very small subsection of boosters to sample and their priorities might not match with many other boosters priorities. Also, the fact that it was discussed and on the list means that there is at least some interest. Anyway, I'm interested in these libraries even though I don't have an app that would use this right this minute. Here's my reasoning. To my way of thinking there is tremendous leverage to be gained in the use of well-tested value types in codification of interfaces, hiding of complex domain rules, and overall simplification of code. Kevlin Henney has written some articles on this (http://www.two-sdg.demon.co.uk/curbralan/papers.html). So on my 'library roadmap' (wiki page: http://tinyurl.com/6v5vx) I have a category called 'value types' where I have a list of these libraries I'd like to see in boost (your's is one of them :-)
I have implemented a type for handling decimal math and a type specialized for monetary values.
decimal64 type ------------- The type stores a decimal value with specified numberof decimals exactly and also performs all arithmetic operations (+,-,*,/) using decimal math.
(Internally the type stores the value as an int64 value which gives 18 DECIMAL digits precision. Multiply and divide uses a 128 bit temporay to avoid rounding errors)
- All std rounding modes are available plus a few extra such as round_to_even.
Can you enumerate 'std rounding modes'?
There are 2 decimal64 types defined:
- decimal64 - all constructors requires specification of number of decimals and optionally rounding mode.
decimal64(3.1415, 4, round_to_nearest);
- decimal64_spec - a template version of decimal64 where number of decimals and rounding mode are specified as template parameters which allows a default constructor.
typedef decimal64_spec<3, round_to_even> decimal3; decimal3 x(3.14), y;
Cool.
...snip...
Some things I would specificly like to have comments on:
1. Operations are not allowed on doubles. Double values always need to be converted into a decimal value first. e.g. x * decimal64(3.14,
2). Is this something that is needed?
Seems reasonable to me.
2. No overflow detection or +inf etc. Just like with the integer types there is no checking for overflow on operations.
Again probably a reasonable compromise.
Money type ...snip...
locale library -------------- Some time ago implemented helper classes for locale but I never got around submitting it to boost. I needed it for testing the decimal64/money streaming so I cleaned it up and it is included in the package.
I'll try and take a look at this as I have some of this type of code for date-time. Jeff

I was at OOPSLA and I think everyone needs to keep in mind that not having a vote at OOPSLA does not imply there is no interest in a library or category. Obviously we had a very small subsection of boosters to sample and their priorities might not match with many other boosters priorities. Also, the fact that it was discussed and on the list means that there is at least some interest.
Anyway, I'm interested in these libraries even though I don't have an app
would use this right this minute. Here's my reasoning. To my way of
Jeff Garland <jeff <at> crystalclearsoftware.com> writes: that thinking
there is tremendous leverage to be gained in the use of well-tested value types in codification of interfaces, hiding of complex domain rules, and overall simplification of code. Kevlin Henney has written some articles on this (http://www.two-sdg.demon.co.uk/curbralan/papers.html). So on my 'library roadmap' (wiki page: http://tinyurl.com/6v5vx) I have a category called 'value types' where I have a list of these libraries I'd like to see in boost (your's is one of them
I understand what you are saying but the comments in this thread (except the discussion Daniel and I have about money implementation) seem to be that there is no need for a decimal type in boost unless it is compatible with the upcoming standard. Since my solution (and your library roadmap) is fixed precision and the standard will be floating point (+fixed via encoding) it is not possible. (And as I said in another post I don't actually see the need for decimal floating point. It solves a couple of theoretical cases where you can use == to compare values but are there any real applications for it?) Money and currency isn't mentioned on your library roadmap and there are no comments here (except from Daniel and he already got a well proven money/currency class that he will make available to boost). Maybe I should concentrate on my locale library instead and add the missing locale_cast functions (e.g. locale::to_string(mydouble, number_format (',','.','3"))

At 02:51 AM 1/11/2005, Martin wrote:
I understand what you are saying but the comments in this thread (except the discussion Daniel and I have about money implementation) seem to be that there is no need for a decimal type in boost unless it is compatible with the upcoming standard.
I don't think the point is that a Boost decimal type has to be compatible with the upcoming decimal TR, but that the Boost decimal type could use the TR types or the Rogue Wave decimal types or whatever as the underlying type.
Since my solution (and your library roadmap) is fixed precision and the standard will be floating point (+fixed via encoding) it is not possible.
The folks pushing the decimal TR have been claiming that the integer subset is robust enough to handle the usual integer operations. Presumably it would include everything needed to implement fixed-point. If you see any technical flaws in the IEEE specs, it would be useful if you could identify them. (Calling it the "Floating-Point Decimal Arithmetic Technical Report" seems to be misleading people, so the name may be changed to just plain "Decimal Arithmetic Technical Report".)
(And as I said in another post I don't actually see the need for decimal floating point. It solves a couple of theoretical cases where you can use == to compare values but are there any real applications for it?)
Some very experienced people believe there is. While I once implemented a lot of business calculations using decimal floating-point, I never had the luxury of having hardware with both binary and decimal floating-point, so don't know if binary would have been more useful than decimal. But since sometimes we were implementing tax related algorithms specified by law as decimal calculations, it was convenient to work in fp-decimal. The tax auditors used fp-decimal hand calculators, so they could easily verify our computations. That was a long time ago and the tax laws may have changed.
Money and currency isn't mentioned on your library roadmap and there are no comments here (except from Daniel and he already got a well proven money/currency class that he will make available to boost).
Well, money/currency may no be on everyone's list, but I'll bet there are a lot of applications where it would be a very helpful addition. --Beman

I don't think the point is that a Boost decimal type has to be compatible with the upcoming decimal TR, but that the Boost decimal type could use the TR types or the Rogue Wave decimal types or whatever as the underlying type.
Well, I got another view. My class implements a decimal type and so does Rogue Wave and TR, all with different approaches. When you select a decimal type you look at the properties of each type and make your choice based on your requirements. If you try to fit everything into one type you would either have to limit yourself to the least common functionality or add workarounds (maybe very inefficient) for not supported functionality of the underlying type. Money is a different matter. There I agree that the the type should allow you to use different underlying decimal (or maybe even binary) types.
Since my solution (and your library roadmap)
is fixed precision and the standard will be floating point (+fixed via encoding) it is not possible.
The folks pushing the decimal TR have been claiming that the integer subset is robust enough to handle the usual integer operations. Presumably it would include everything needed to implement fixed-point.
That is my view as well and that is why I created my decimal64 type. Do you mean that we don't need a fixed decimal type since everyone can implement it themselves using integers?
(And as I said in another post I don't actually see the need for decimal floating point. It solves a couple of theoretical cases where you can use == to compare values but are there any real applications for it?)
Some very experienced people believe there is. While I once implemented a lot of business calculations using decimal floating-point, I never had the luxury of having hardware with both binary and decimal floating-point, so don't know if binary would have been more useful than decimal. But since sometimes we were implementing tax related algorithms specified by law as decimal calculations, it was convenient to work in fp-decimal.
Laws are different everywhere ofcourse but the ones I have seen specify the number of decimals to use in a calculation and not the number of digits. Did you use a fp-decimal type where you could limit the size of the mantissa?

At 02:28 AM 1/12/2005, Martin wrote:
The folks pushing the decimal TR have been claiming that the integer subset is robust enough to handle the usual integer operations. Presumably it would include everything needed to implement fixed-point.
That is my view as well and that is why I created my decimal64 type. Do you mean that we don't need a fixed decimal type since everyone can implement it themselves using integers?
...
don't know if binary would have been more useful than decimal. But since sometimes we were implementing tax related algorithms specified by law as decimal calculations, it was convenient to work in fp-decimal.
Laws are different everywhere ofcourse but the ones I have seen specify
Martin, we are going over a lot of the same ground already covered in the review of Bill Seymore's library. I think you should read through a lot of those comments. the
number of decimals to use in a calculation and not the number of digits. Did you use a fp-decimal type where you could limit the size of the mantissa?
No. The calculations involved petroleum products like propane, with volumes ranging from a few gallons to whole underground caverns full of the stuff. The volumes have to be temperature compensated with a coefficient of expansion. It gets messy. --Beman

On Tue, 11 Jan 2005 07:51:26 +0000 (UTC), Martin wrote
I understand what you are saying but the comments in this thread (except the discussion Daniel and I have about money implementation) seem to be that there is no need for a decimal type in boost unless it is compatible with the upcoming standard. Since my solution (and your library roadmap) is fixed precision and the standard will be floating point (+fixed via encoding) it is not possible. (And as I said in another post I don't actually see the need for decimal floating point. It solves a couple of theoretical cases where you can use == to compare values but are there any real applications for it?)
Every application that requires calculations with monetary values -- I honestly don't know how anyone that implements with the current floating point types -- the error is totally unpredictable. Just plain not acceptable in the financial domain. So you either roll your own, buy it from Roque Wave, go with JAVA, or get your name in the newspaper when the app causes a multi-million dollar accounting error :-0
Money and currency isn't mentioned on your library roadmap and there are no comments here (except from Daniel and he already got a well proven money/currency class that he will make available to boost).
You must have missed it -- under ValueTypes there is the 'currency' library (top left -- middle of diagram). The only thing described in the bullets is the categories. We explicitly dicussed currency at OOPSLA. A few people mentioned having a need for it at one point or another.
Maybe I should concentrate on my locale library instead and add the missing locale_cast functions (e.g. locale::to_string(mydouble, number_format (',','.','3"))
I, for one, would be sorry to see that. Between you and Daniel seems like we have the potential to get a very valuable library (or 2). Jeff

Martin writes:
I understand what you are saying but the comments in this thread (except the discussion Daniel and I have about money implementation) seem to be that there is no need for a decimal type in boost unless it is compatible with the upcoming standard. Since my solution (and your library roadmap) is fixed precision and the standard will be floating point (+fixed via encoding) it is not possible. (And as I said in another post I don't actually see the need for decimal floating point. It solves a couple of theoretical cases where you can use == to compare values but are there any real applications for it?)
Why don't you take a look at the decimal library's formal review posts? They discuss the issue in detail. -- Aleksey Gurtovoy MetaCommunications Engineering
participants (10)
-
Aleksey Gurtovoy
-
Beman Dawes
-
Daniel Frey
-
Jeff Garland
-
Jonathan Turkanis
-
Martin
-
Noah Stein
-
Rob Stewart
-
Stefan Slapeta
-
Stefan Slapeta