[Tim]
>> But the decimal spec takes a different approach, which Python's docs
>> don't explain at all:  the otherwise-mysterious ROUND_05UP rounding
>> mode.  Quoting from the spec:
>>
>>     http://speleotrove.com/decimal/damodel.html
>>     ...
>>     The rounding mode round-05up permits arithmetic at shorter
>>     lengths to be emulated in a fixed-precision environment without
>>     double rounding. For example, a multiplication at a precision of 9
>>     can be effected by carrying out the multiplication at (say) 16
>>     digits using round-05up and then rounding to the required length
>>     using the desired rounding algorithm.
>>
>> In your original example,  1.01 * 1.46 rounds to 4-digit 1.474 under
>> ROUND_05UP. and then `quantize()` can be used to round that back to 1,
>> 2, or 3 digits under any rounding mode you like.
>>
>> Or, with your last example,
>>
>> >>> with decimal.localcontext() as ctx:
>> ...     ctx.rounding = decimal.ROUND_05UP
>> ...     r = D('1.00000000000001')*D('1.49999999999996')
>> >>> r
>> Decimal('1.499999999999974999999999999')
>> >>> r.quantize(D('.00000000000001'))
>> Decimal('1.49999999999997')

[Marco Sulla <marco.sulla.pyt...@gmail.com>]
> And can't be this the default of decimal?

Try to spell out what you mean - precisely! - by "this". I can't do
that for you. For any plausible way of fleshing it out I've thought
of, the answer is "no".

That's quite beyond that we don't have a blank slate: at this point,
_nothing_ about the default behavior of `decimal` can be changed
without breaking mounds of code.  Won't happen.

> For what I know, this is the default of BigDecimal in Java:

Again, I don't know what "this" means.

> ...
> Example online:  http://tpcg.io/5axMxUQb

That example's output is radically different than in the example you
quoted: the result displayed is:

1.4999999999999749999999999996

not the

1.499999999999974999999999999

in the example you quoted.

BigDecimal, by default, produces results with an unbounded number of
significand digits.  29 in this specific case. The entire point of the
original example is that Python's `decimal` defaults to 28 maximum, so
needs to round away the trailing "6" from the infinitely precise
result. That can lead to "double rounding" errors when that's rounded
back again.

The cleverness of ROUND_05UP - which appears to be senseless at first
sight - is that it manages to use the last retained digit to _encode_
enough information about the infinitely precise result that a second
rounding to a narrower precision gives exactly the same result as if
it were given the infinitely precise result to work on.  Regardless of
whether the second rounding is to nearest/even, half-up, toward 0,
to-plus-infinity, .... It even has exactly the same effects on the
inexact flag.  Its only purpose is to eliminate "double rounding"
errors - as a standalone rounding mode, ROUND-05UP is worse than
useless (it _appears_ to be a bizarre mix of round-toward-0 and
round-away-from-0).

BigDecimal is most naturally suited to fixed point. The number of
significand digits varies dynamically, without upper bound, to try to
preserve (via a maze of rules) the position of the decimal point as a
function of operations' inputs' decimal point locations. It can be
used to emulate floating point, but that requires passing MathContext
objects too, over & over & over, to keep rounding away unwanted
precision.

Python's decimal is most naturally suited to floating point. The
maximum number of significand digits is fixed (although
user-settable), and the scale factor ("exponent") varies dynamically
to keep the number of significand digits in bounds. It can be used to
emulate fixed point, but that requires doing other stuff over & over &
over, to keep forcing the decimal point back to the fixed location the
user has in mind.

The closest you can get to BigDecimal's behavior "by magic" in Python
is to set the context precision to its maximum allowed value. But very
few people would actually want that! Behold:

>>> import decimal
>>> decimal.Decimal(1) / decimal.Decimal(3)
Decimal('0.3333333333333333333333333333')

That doesn't surprise anyone. But this would:

>>> decimal.getcontext().prec = decimal.MAX_PREC
>>> decimal.Decimal(1) / decimal.Decimal(3)
Traceback (most recent call last):
...
MemoryError
>>>

That's because:

>>> decimal.MAX_PREC
999999999999999999

is huge on a 64-bit box, and nobody has that much RAM.

BigDecimal isn't actually better in this respect: try the similar
thing in Java, andi it throws java.lang.ArithmeticException, with
detail "Non-terminating decimal expansion; no exact representable
decimal result.".

Approximately nobody wants that as a default behavior.  That's why
when you see actual Java code doing divisions with BigDecimal, they
almost always use the overload that requires passing an explicit
MathContext object too, to force a small maximum on the number of
significand digit\s that will be retained.
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/6XBALAQDDE2PPKX4QOTD7VE32UHW5APV/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to