[Python-ideas] isinstance(Decimal(), Real) -> False?
Oscar Benjamin
oscar.j.benjamin at gmail.com
Thu Aug 29 15:19:45 CEST 2013
On 29 August 2013 09:13, Draic Kin <drekin at gmail.com> wrote:
> On Thu, Aug 29, 2013 at 4:02 AM, Steven D'Aprano <steve at pearwood.info> wrote:
>> On 28/08/13 20:48, Draic Kin wrote:
>>
>>> and issubclass(Rational, Real) -> False. It's more about exact vs.
>>> non-exact computations which is orthogonal to number hierarchy.
>>
>>
>> The numeric tower is precisely about the numeric hierarchy of Number >
>> Complex > Real > Rational > Integral, and since they are all *abstract* base
>> classes, exact and inexact doesn't come into it. Concrete classes can be
>> inexact or exact, or one could implement separate Exact and Inexact towers.
>>
>> In practice, it's hard to think of a concrete way to implement exact real
>> numbers. Maybe a symbolic maths application like Mathematica comes close?
Yes, Mathematica and more pertinently sympy implement exact real
numbers. All fixed- or floating-point number formats are restricted to
representing rational numbers. The obvious examples of exact
irrational numbers are things like pi, e, sqrt(2) etc. Sympy can
represent these exactly and guarantee that sqrt(n)**2 is exactly n for
any integer n.
>>> Maybe there
>>> should be some ExactNumber abstract base class and some convention that
>>> exact shouldn't coerce with non-exact. So Decimal + float should raise an
>>> exception even if both would be subclasses of Real (and Decimal even of
>>> Rational). Or maybe it would be enough if there were just non-exact
>>> variants of Real and Complex since non-exactness if just issue of them.
>>
>> Personally, I would implement inexact/exact as an attribute on the type:
>>
>> if type(x).exact: ...
>>
>> sort of thing.
Exactness is more complicated than that. Whether or not operation(a,
b) is exact depends on:
1) the operation
2) the types of a AND b
3) the values of a and b
For example if both operands are ints then results are exact for
addition, subtraction, multiplication, and sometimes for division.
Exponentiation is exact where the exponent is a non-negative integer
but not if the exponent is negative. Fractions are exact for division
also (except by 0) and for exponentiation where the exponent is any
integer but not if the exponent is a non-integer valued Fraction (even
if exact results are possible). int(num) is not exact unless num is an
integer. Fraction(num) is always exact (or an error). The only senses
in which Decimals are exact are that Decimal(str, Decimal(int) and
Decimal(float) are exact and - if you set the Inexact trap - you can
get an error any time something inexact would have otherwise occurred.
(Well you could say that decimals are "exactly rounded" but that's not
what we mean by exact here).
> There were some points (possible against the idea of issubclass(Decimal,
> Real) -> True) like Decimal doesn't coerce to float,
Thinking about it now I would rather that float/Decimal operations
coerce to Decimal and a FloatOperation error to be raised if set (by
someone who doesn't want to mix Decimals and floats).
> you cannot convert
> Fraction to Decimal easily, you cannot sum Fraction + Decimal.
These are just missing features IMO. Presumably if Decimal had been
integrated into the numbers hierarchy these would have been added.
> Maybe some of
> the rationale for the behavior is the matter of exact vs. non-exact.
Decimals are not exact!
> So for
> that reason the exactness rationale could be made explicit by adding some
> indicator of exacness and base some coercion cases on this indicator.
The coercion may be exact but the subsequent arithmetic operations are
not. Preventing mixed Fraction/Decimal arithmetic does not save you
rounding error:
>>> d = decimal.Decimal('.1234')
>>> f = fractions.Fraction('1/3')
>>> d
Decimal('0.1234')
>>> f
Fraction(1, 3)
>>> d * f
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'Fraction'
Well it's a good thing that type coercion saved us from being able to
get rounding errors. Hang on...
>>> d / 3
Decimal('0.04113333333333333333333333333')
That's the exact same rounding error we would have got with the Fraction!
Any Decimal whose precision exceeds the current context precision will
not have exact arithmetic operations (for any operation):
>>> d = decimal.Decimal(11**30)
>>> (d/3)*3 == d
False
Oscar
More information about the Python-ideas
mailing list