[boost] Re: Fixed-Point Decimal Formal Review
Despite the several yes votes (thanks), I think I have to agree with Ilya and vote no for my own library. Of the problems that have been mentioned, two, IMO, are show-stoppers: the problem with the scale being immutable resulting in inequality after assignment (if the scale isn't part of the type), and the need for a floating-point decimal type to hold intermediate results. (Ilya, you're right that I haven't responded to the latter issue. It's not that I haven't read your posts, but rather that I didn't think I had anything intelligent to say yet. Please don't take it personally.) Also, I got a note this morning from Raymond Mak of IBM in Toronto who will be proposing a floating-point decimal TR at the J11/WG14 session in Kona. It's based on the proposed IEEE 754 that Mike Cowlishaw mentioned in another Boost post. So even if the current version of the library is accepted into Boost, I'll be going back to the drawing board anyway. Maybe acceptance is premature at this point. --Bill Seymour ___ Unsubscribe other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
[boost] Fixed-Decimal's Scale
I've been thinking about the related issues of whether decimal should be a template and whether the scale should be immutable. First, I'm adamant that the user should be able to write dollars = foo; without magically losing pennies or gaining mils; and I don't think the user should have to type something like dollars.assign_without_changing_frac_digits(foo); as a workaround to get that behavior. Given that, then as Daryle has correctly pointed out, standard library sequence collections of decimals, and mutating algorithms that work on sequences of decimals, don't work right unless all objects in the sequence have the same scale. This issue of working right must be addressed before we worry about efficiency or the size of the objects. 1. The scale could be part of the type (decimal becomes a class template). 2. A warning could be put in the documentation that says that storing decimals of different scales in the standard collections yields undefined behavior. 3. I could try to work some other kind of magic. The main difference I see between 1 and 2 is whether it's possible for the user to make the mistake. In neither can we have collections of different scales; but I now think that ought to be allowed if there's some way I can make it work. (Note, for example, that with either of the first two solutions, 1, 1.2 and 1.23 can't be sorted!) So I'd like to try 3, at least as a mind experiment, before deciding on 1 or 2. Note that copy construction works correctly; it's just assignment that has the perverse behavior. Let's say I specialize std::swap() to swap scale as well as value, and then specialize other standard algorithms to use swap where they might otherwise use assignment. Have I done enough? --Bill Seymour ___ Unsubscribe other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
[boost] Re: Thoughts on fixdec
Fernando Cacciola wrote: I'm inclined toward boost::numeric. Two votes for boost::numeric. I prefer just 'decimal' because most decimal numbers I know of are fixed-point. So I think 'fixed' can be left implicit. Two votes (including mine) for decimal; two votes (including Michel Andre's from a while back) for fixed_decimal. Couldn't [overflow/underflow checks] be provided as a postcondition, in terms of BOOST_ASSERT? I'll consider that. Fernando: Me: Daryle: 6. There should only be conversions from strings, and _no_ mixed operations with strings. In the absence of decimal literals, I think it's easier for the user to be able to use strings as pseudo-literals in all contexts; and I don't see how the mixed operations do any harm. The problem is that quite unfortunately, string literals are just pointers to constant char, but such pointers are also used to access raw memory, so, char const* is the target to implicit conversions from a lot more than string literals. This would lead to unexpected implicit conversions. But I think that C++ inherits that part of the spirit of C that says, Trust the programmer. It's not that all programmers are trustworthy, nor even that some programmers are always trustworthy. It's that many programmers become increasingly trustworthy as they gain more experience; and we want to reward that behavior. 8-) Fernando: Me: Daryle: 13. You have strange (regular) assignment semantics. You try to change the receiver's logical value to the source's logical value, but retain the receiver's original scale (internally shifting the new significand and doing any rounding if needed). ... you will get strange effects if you try to change containers of decimal through assignment. I thought it would be less surprising if a decimal object's scale were immutable. This matches prior art (the BCD type on IBM mainframes, for example) in which the scale is part of the type. You're right about containerdecimal; but remember that this class isn't intended for number-crunching; so I don't really care about assigning matrices, etc. But this isn't about assigning matrices and number-crunching, it's about C++ assignment semantics. The postcondition of equivalence for assignment is a strongly fundamental feature of the C++ object model, and everything and everyone, not just containers, relies on that. Hmm... I came up with two use cases: figuring the final payment of a loan and converting currency. In both cases, I had to write the code very carefully to /create/ the error I was worried about; and even after doing so, neither case was particularly compelling. You might have convinced me. Also, when considering the case of converting currencies, I saw a problem with multiplication not being commutative: decimal dollars(2, 314.75); decimal exchange_rate(6, 0.890004); decimal euros(2); #if ONE_WAY_TO_DO_IT euros [round_up]= exchange_rate * dollars; #else euros = dollars [round_up]* exchange_rate; #endif Programmers would have to be very careful with such code in any event; so maybe my argument isn't a good one. I still have an /emotional/ attachment to it, though. I'll work on that. 8-) Question: should I go back to templateint Scale class decimal? Advantages: The assignment semantics issue goes away because the scale is part of the type. It's more like prior art. Disadvantages: The scale must be a constant expression. There are member template issues with some compilers. It won't work at all with MSVC v5 which can't deduce non-type template arguments. (OK, I could bite the bullet and maintain a non-Boost version for my own use at work; but I wouldn't be happy about it.) --Bill Seymour ___ Unsubscribe other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
[boost] Re: Questions on fixdec
Ilya, 1. You want the scale to be a template parameter so that it's part of the type. I've just raised this issue again; and I expect there to be some discussion about it. Stay tuned. 8-) 2. You also want the rounding mode to be part of the type. That binds the rounding mode to the operand instead of to the operator; but I've already chosen the latter. We could reopen that discussion if you want to; but I haven't gotten any other complaints. 3. You want the internal representation to be a single 64-bit integer to make the object smaller. Why? For efficiency? Do you think it would be faster to calculate the representation of unity for each operation? For decreased memory usage? Are you doing embedded systems work? Somebody said, Premature optimization is the root of all evil. I can't remember who that was (Knuth? Djikstra?); but somebody else on the list will probably tell us. 4. I haven't looked at your example code in detail yet; but at first glance, it looks like you're confusing the precision (the maximum number of representable digits) with the scale (the number of digits to the right of the decimal point). --Bill Seymour ___ Unsubscribe other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
[boost] Re: Formal Review: fixed-point decimal library
Paul Bristow wrote: Overall I vote to accept this in the Boost library. Thanks. There are some warnings using 'strict' which are probably cast avoidable: fixed_decimal.hpp(442) : warning C4389: '==' : signed/unsigned mismatch fixed_decimal.hpp(446) : warning C4018: '' : signed/unsigned mismatch fixed_decimal.hpp(450) : warning C4018: '' : signed/unsigned mismatch Fixed. I believe the best namespace is numeric. Three votes for boost::numeric. It looks like that's the consensus. I really don't like the name scale at all. Isn't that the correct term? Nor deffrac, decscale and iosprec. Can't we do better than this, even if longer? Suggestions? The documentation is mainly pitched at a too high a level for users ... [other suggestions] OK, I'll try to simplify it and add more examples. As regards the need for a decent test program, with which I agree completely, my plan now is to have three of them. - One, built around the Boost test framework, just tests whether the various operators and free functions are doing the arithmetic right. - A second, which instantiates all the output-related templates, writes some interesting values like numeric_limitsdecimal::min() and tests the correct operations of the I/O manipulators and rounding modes (as arguments to operator), locales, and narrow and wide streams. - A third which instantiates all the input-related templates and does the same kind of tests except that the user will have to enter some ad hoc values. For the second and third test programs, because locale strings are implementation-specific, they'd be entered as command-line arguments. Does that seem sufficient? --Bill Seymour ___ Unsubscribe other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
[boost] Re: Thoughts on fixdec
the scale is part of the type. You're right about containerdecimal; but remember that this class isn't intended for number-crunching; so I don't really care about assigning matrices, etc. 14. If you have a scale member function to return the current object's scale; you should have something like a significand member function to return the object's mantissa. This will allow copies of objects that retain just one aspect of their source. 15. It took me a while to figure the following out, so maybe you should put it in simpler language. Objects of this type retain a copy of the given value, rounded (via the given mode) and kept to a precision of the given scale number of (base-ten) fractional digits. The value is stored as that scale and the (integral) number of 10**(-scale) units needed to add up to the value. I'm not sure what you mean. I guess that the scale needs to be non-negative? Yes, and that's pointed out in the documentation, but maybe not loudly enough. 16. ... the [] is bound to the object on the left (by C++'s parsing rules), and not to the operator. You just adjust the spacing and wish that the brackets were associated with the operator on the right. Yes, and that's pointed out in the documentation with a box around it; and there's a rather long bit (maybe even too long) that explains how the whole business came into being. 17. Do you really need to support VC++5 ...? Yes, I do, because that's what I'm stuck with at work (for what are euphemistically called non-technical reasons). I understand completely that such support is not required for Boost; but absence of a requirement isn't a requirement of absence; and I don't see that the additional support does any harm. (Users have to hack their visualc.hpp to let that version pass anyway.) Thanks for the input. --Bill Seymour ___ Unsubscribe other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
[boost] Re: Formal Review: fixed-point decimal library
Fernando, Thanks for your input. The scale really is intended to be the number of decimal digits to the right of the decimal point; so, yes, if int_type on your system is 64 bits, and if you specify a scale of 18, then no /whole/ decimal digits will be allowed to the left of the point. (You might get a 19th digit if it isn't too big.) One of your tests, for example: s=2147483647 [9,s]=2147483647.0 [10,s]=302809239.6290448384 Trying to construct the decimal with a scale of 10 results in signed integer overflow which yields undefined behavior. Here is a list of other minor issues: = I don't think that is_bounded and is_modulo should be inherited from int_type. is_bounded is intended to be false only for those types which can represent numbers in any range. Even if int_type were unbounded (is_bounded=false), decimal itself will always be bounded, ... Why? Similarly, is_modulo tells whether the numeric type uses modulo arithmetic, as do the unsigned integral types, yet decimal does not do that ... I don't see this. If int_type just wraps around on overflow, then why wouldn't the decimal type itself do the same? ... even if int_type where unsigned, ... int_type can't be unsigned. Currently, the library cannot be put in a precompiled header using BCC because of the 'max_val,min_val' constants. This can be solved by making those constants inlined functions. But then they wouldn't be constants, would they? The documentation is clearly written with some 'old' version in mind. I think all references and comparisons with that older version should be removed from the docs, or at least, put aside on a 'history' section. OK. IMO the documentation should begin with a brief (but complete) overview of what is a 'fixed decimal' number, including the concept of a 'scale' (or 'digits' as I much prefer), and of rounding modes. I was reticent to do this for fear of talking down to my audience. I can add more introductory material if others think it would be a good idea. I understand why it is not supported to construct a decimal from an int_type, but I think that construction from 'unsigned int' should be supported ... I'll be happy to put that in if you can give me a use case for it. The docs say: The conversion will be exact if the string contains no more than scale digits to the right of the decimal point; otherwise the value will be rounded as specified This is incorrect, I think. If the string contains more than (scale+1) digits, whether to the left or right of the decimal point (or both), rounding will ocurr. I think the documentation is basically correct; although if n * scale**10 can't fit in an int_type, then the behavior is undefined; so I guess rounding could occur in that case. 8-) --Bill Seymour ___ Unsubscribe other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
[boost] Re: Formal Review: fixed-point decimal library
Fernando Cacciola wrote: OK. So, correct me if I'm wrong: For any given decimal with some int_type, a 'digits' constant is associated with it which comes from numeric_limitsint_type::digits10. This constant identifies the maxium number of digits (in both the whole and fractional parts) that any such decimal can represent. Right, it's the /precision/ (the total number of representable digits). Consider, for example, numeric_limitsint::digits10 on an implementation with 32-bit ints. This value will be 9 because all 9-digit values are representable. There will also be some some 10-digit values that are representable (if the MSD is in [-2,2]). Thus numeric_limitsdecimal::digits and digits10 are equal to this constant. Yes, and the two values are equal to each other because numeric_limitsdecimal::radix is 10. The scale 's' given by the user specifies the desired number of decimal digits. I would say digits to the right of the decimal point. (Digits to the left of the point I would also call decimal because they're base-10.) It cannot be '(digits-1'), ... Yes, the scale can be equal to the precision (which I believe is what you're calling digits); but then, in general, there won't be any digits to the left of the point. (As with the example above with numeric_limitsint::digits10, there could be one valid digit to the left of the point if it's not too large.) I'd put a description like the one above in the docs so that the limit on total number of digits and its relation with the scale is well documented. Agreed. Why? [can't decimal be unbounded] Because it has a constant max_scale. Ah, so. Yes, I see that now. Good catch. I don't see this. If int_type just wraps around on overflow, then why wouldn't the decimal type itself do the same? Because decimal has its own arithmetic. For instance, would decimal::add/mul wrap around even if int_type would? Yes, I think you're right about that. OK, is_bounded should be true and is_modulo should be false. I'll fix that. Re the max_val, min_val and max_scale constants: there was some reason that I wanted them to be constant expressions. I'll reconsider that; but I don't want to promise a change right now. Re construction from unsigned int: I think you gave a good reason to allow it. I'm pretty busy at my day job right now, so I can't promise a quick change; but I agree with you. If the concept of 'digits' is formalized, alon with its relation to 'scale', then this statement could simply say that The conversion will be exact if the string contains no more than 'digits()' digits (whole and/or decimal) I don't think that's right. I can assign 123.45 to a decimal with a scale of 2 without rounding. Do I misunderstand what you're saying? --Bill Seymour ___ Unsubscribe other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
[boost] Re: Formal Review: fixed-point decimal library
I've upload fixdec23.zip to the Yahoo files section. I made some changes that Jens suggested to allow compiling with Comeau and gcc; and I've made the is_bounded and is_modulo fixes that Fernando suggested along with interoperatability with unsigned as well as int. Fernando, I think that max_scale needs to be a constant expression because it's used in the definition of numeric_limitsdecimal: BOOST_STATIC_CONSTANT(int, digits=boost::fixdec::max_scale); BOOST_STATIC_CONSTANT(int, digits10=boost::fixdec::max_scale); And I think I'm right about numeric_limits::digits10. I believe it's intended to be mean that, if the value is N, then /all/ N-digit numbers can be represented. numeric_limitsdecimal::digits10 can't be 19 because only a subset of 19-digit numbers are representable. Also, I think that your statement: ... __int64 support 18 digits at most. is factually incorrect. Your implementation's __int64 should support anything up to 10**63 - 1 or (with commas as thousands separators) 9,223,372,036,854,775,807. That's a 19-digit number. --Bill Seymour ___ Unsubscribe other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
[boost] Formal Review Request, Fixed-Point Decimal Library
Now that I once again have the time to pay attention to what's going on in Boost, I'd like to request a formal review of the fixed-point decimal library in fixdecv2.zip in the Yahoo files section. I haven't made any changes since I uploaded that file last October. Thanks, --Bill Seymour ___ Unsubscribe other changes: http://lists.boost.org/mailman/listinfo.cgi/boost