[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.Python@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.