
On 5/21/05, Raymond Hettinger <raymond.hettinger@verizon.net> wrote:
A root difference is that I believe we have both a compliant implementation (using Context.create_decimal) and a practical context free extension in the form of the regular Decimal constructor.
Please forgive an intrusion by someone who has very little knowledge of floating point pitfalls. My mental model of Decimal is "pocket calculator arithmetic" (I believe this was originally prompted by Tim, as I had previously been unaware that calculators used decimal hardware). In that model, fixed precision is the norm - it's the physical number of digits the box displays. And setting the context is an extremely rare operation - it models swapping to a different device (something I do do in real life, when I have an 8-digit box and am working with numbers bigger than that - but with Decimal, the model is a 28-digit box by default, and that's big enough for me!) Construction models typing a number in, and this is where the model breaks down. On a calculator, you physically cannot enter a number with more digits than the precision, so converting a string with excess precision doesn't come into it. And yet, Decimal('...') is the "obvious" constructor, and should do what people "expect". In many ways, I could happily argue for an exception if the string has too many digits. I could also argue for truncation (as that's what many calculators actually do - ignore any excess typing). No calculator rounds excess input, but I can accept it as what they might well do if was physically possible. And of course, in a practical sense, I'll be working with 28-digit precision, so I'll never hit the situation in any case, and I don't care :-)
A second difference is that you see harm in allowing any context free construction while I see greater harm from re-introducing representation error when that is what we were trying to fix in the first place.
The types of rounding errors (to use the naive term deliberately) decimal suffer from are far more familiar to people because they use calculators. With a calculator, I'm *used* to (1/3) * 3 not coming out as exactly 1. And indeed we have
(Decimal(1)/Decimal(3))*Decimal(3) Decimal("0.9999999999999999999999999999")
Now try that with strings:
(Decimal("1")/Decimal("3"))*Decimal("3") Decimal("0.9999999999999999999999999999") (Decimal("1.0")/Decimal("3.0"))*Decimal("3.0") Decimal("0.9999999999999999999999999999")
# Remember, my argument is that I'd never do the following in
Nope, I don't see anything surprising. After a bit more experimentation, I'm unable to make *anything* surprise me, using either Decimal() or getcontext().create_decimal(). Of course, I've never bothered typing enough digits that I care about (trailing zeroes don't count!) to trigger the rounding behaviour of the constructor that matters here, but I don't ever epect to in real life. Apologies for the rambling discussion - it helped me as a non-expert to understand what the issue is here. Having done so, I find that I am unable to care. (Which is good, because I'm not the target audience for the distinction :-)) So, to summarise, I can't see that a change would affect me at all. I mildly favour Tim's position - because Raymond's seems to be based on practicality for end users (where Tim's is based on convenience for experts), and I can't see any practical effect on me to Tim's change. OTOH, if end user impact were the driving force, I'd rather see Decimal(string) raise an Inexact exception if the string would be rounded: practice, so this is
# solely for a highly unusual edge case! decimal.getcontext().prec=5
# This confuses me - it silently gives "the wrong" answer in my mental model. Decimal("1.23456789") * 2 Decimal("2.4691")
c = decimal.getcontext().copy() c.traps[decimal.Inexact] = True
# This does what I expect - it tells me that I've done something wrong! c.create_decimal("1.23456789") * 2 Traceback (most recent call last): File "<stdin>", line 1, in ? File "C:\Apps\Python24\lib\decimal.py", line 2291, in create_decimal return d._fix(self) File "C:\Apps\Python24\lib\decimal.py", line 1445, in _fix ans = ans._round(prec, context=context) File "C:\Apps\Python24\lib\decimal.py", line 1567, in _round context._raise_error(Inexact, 'Changed in rounding') File "C:\Apps\Python24\lib\decimal.py", line 2215, in _raise_error raise error, explanation decimal.Inexact: Changed in rounding
I hope this helps, Paul.