On 5 April 2016 at 18:08, Aaron Meurer <asmeu...@gmail.com> wrote:
> On Tue, Apr 5, 2016 at 12:54 PM, Oscar Benjamin
> <oscar.j.benja...@gmail.com> wrote:
>>
>>>
>>> I don't know if it should be considered a bug, but it's worth noting
>>> that if you want SymPy to give the right precision in general you have
>>> to start with Float objects that are set with the precision you need.
>>> To me it feels like a bug because it negates the purpose of the evalf
>>> precision argument.
>>
>> Is there a coherent policy on float-handling in sympy?
>>
>> My ideal would be:
>>
>> 1) All float objects are created exact (having the exact value of the
>> object passed in).
>> 2) No inexact auto-evaluation.
>> 3) .evalf() can be used to fully evaluate the expression with desired 
>> precision.
>> 4) Ideally the precision argument to .evalf would be the precision of
>> the *output* rather than the internal precision of the intermediate
>> calculations
>
> Can you clarify what you mean by "exact" here?
>
> Note that there's no way to know what the input value of a float is.
> That is, there's no way to write Float(0.2) (with no quotes) and have
> it be treated as Float(2/10).  The 0.2 object is converted to a Python
> floating point by the time that Float sees it, and it's not a decimal:
>
> In [49]: (0.2).as_integer_ratio()
> Out[49]: (3602879701896397, 18014398509481984)

Passing a Python float into a sympy expression (I know I accidentally
did it earlier in the thread) is usually not going to do what is
wanted e.g. 0.1*x creates a number not truly equal to 0.1 and passes
it to x.__rmul__. The good fix for this is as you say to use a string.
However there are times when it would be good to pass in a float that
you have obtained from some other calculation and have sympy work with
it.

Currently Sympy will round an input float to 15d.p. and as a result
S(0.1) will result in an mpf which really does have a true value of
0.1. This is useful for novices but IMO just hides the binary float
problem a bit. The right solution is for users to understand that they
should be using S("0.1") or something.

If OTOH I received my float from some other source (rather than trying
to make a simple number like 0.1) then sympy is rounding the number
rather than taking it in exactly. I would prefer it in this case if
sympy would retain the exact value of the input number the same way
that Fraction and Decimal do:

In [19]: from fractions import Fraction

In [20]: Fraction(0.1)
Out[20]: Fraction(3602879701896397, 36028797018963968)

In [21]: from decimal import Decimal

In [22]: Decimal(0.1)
Out[22]:

Decimal('0.1000000000000000055511151231257827021181583404541015625')
In [23]: from sympy import Float

In [24]: Float(0.1)
Out[24]: 0.100000000000000

Both Fraction and Decimal always retain the exact input value. If you
want to round it then you need to do that explicitly. This is useful
because once you're numbers are converted to say Decimal then you can
use the calculation contexts to control exactly how the rounding
occurs (or to prevent any rounding) etc. If the rounding occurs
straight away at input then you cannot.

> That's why Float allows string input (and it's the recommended way of
> creating them).
>
> With that being said, I don't think the fact that
> (1.4142).as_integer_ratio() isn't (7071, 5000) is the problem here.
> Float(1.4142) is indeed inexact compared to Float('1.4142'), but the
> wrong answers from x**6000%400 come from lack of computing precision,
> not lack of input accuracy.

It is a separate but related issue to this. In this particular case
S(1.4142) does what a user may be intending it to do and creates
mpf("1.4142") so the issue occurs later.

>> Currently 1) already occurs for decimal strings but Float(float)
>> rounds to 15 digits and you can explicitly force something impossible
>> as a ratio string: Float("1/3"). I think Float should be more like
>> decimal.Decimal here: all input arguments are treated as exact
>> regardless of precision etc. (and I don't see any good reason for
>> allowing Float("1/3"))
>>
>> Without 2) it is impossible to achieve 3). If approximate
>> auto-evaluation can occur before calling .evalf then there's no way
>> for evalf to set the precision to be used for the auto-evaluation.
>>
>> Obviously 4) is harder than current behaviour and perhaps impossible
>> in general but it is achievable for simple cases like in this thread.
>> From a user perspective it is definitely what is actually wanted and
>> much easier to understand.
>
> I'm unclear how this works, because if you take my example above with
> x = nsimplify("1.4142"), evalf() gave the right answer with the
> default precision (15). That is, when everything in the expression is
> a non-float, it gives the right answer. However, it seems that as soon
> as an expression contains a Float, that Float must have whatever
> precision you need set on it.

I assume that you mean this:

In [4]: ((nsimplify("1.4142") ** 6000) % 400).evalf()
Out[4]: 271.048181008631

So let's pull that apart:

When you do nsimplify("1.4142") you get back a Rational.
Exponentiation that with an integer gives another Rational. The
expression is auto-evaluated to get the new rational but it is done
exactly. Likewise the modulo 400 is auto-evaluated but again it gives
an exact Rational.

Print this to see the massive numerator/denominator that are created:
    nsimplify("1.4142") ** 6000 % 400

Since the final .evalf() is only called on a Rational which was itself
computed exactly and can easily give as many correct digits as you
desire.

However when you do this:

In [13]: Float("1.4142") ** 6000 % 400
Out[13]: 32.0000000000000

It works out differently. So what happens here is the expression
Float("1.4142") ** 6000 is auto-evaluated but only using the default
precision that is attached to the mpf Float (or maybe the context):

In [15]: Float("1.4142") ** 6000
Out[15]: 1.16144178843571e+903

So here we have auto-evaluation that has changed the numeric value of
the expression. This is not the same as simplifying 2**3 -> 8 or some
other simplification that is *exactly* correct because it has
introduced a numerical error. I think that this kind of
auto-evaluation should not occur by default.

So in the ideal scenario any auto-evaluation/simplification that would
not be exactly correct should not be applied. Then at the end when
calling .evalf it is possible to specify exactly the precision used
for *all* of the approximate steps in the calculation.

However as I said earlier a better general approach (although harder)
would be to have an API that allows to specify the accuracy of the
desired answer rather than the precision used for calculations. So in
this case it works back recursively. I have an expression:

    s = Mod(Pow(Float("1.4142"), 6000), 400)

and I call

    s.evalf(correct_digits=10)

and then this recursively calls evalf along the tree asking each time
for the number of correct digits needed in order to ensure that the
*end result* is correct to 10 digits.

--
Oscar

-- 
You received this message because you are subscribed to the Google Groups 
"sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sympy+unsubscr...@googlegroups.com.
To post to this group, send email to sympy@googlegroups.com.
Visit this group at https://groups.google.com/group/sympy.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sympy/CAHVvXxQrYO28QFfvPH3GQXGx-DZ6QwRhEeH-J3-hacsaCjDMBw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to