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.