On Sun, Jun 10, 2018 at 10:48 PM, Steven D'Aprano <st...@pearwood.info> wrote:
> > > In regard to the "special values", and exact results -- a good math lib > > should return results that are "exact" in all but maybe the last digit > > stored. So you could check inputs and outputs with, e.g. math.isclose() > to > > give people the "exact" results. -- and keep it all in floating point. > > I wish Uncle Timmy or Mark Dickinson were around to give a definite > answer, but in their absence I'll have a go. I'm reasonably sure > that's wrong. > hmm -- I'm no numerical analyst, but I could have sworn I learned (from Kahan himself) that the trig functions could (and were, at least in the HP calculators :-) ) be computed to one digit of accuracy. He even proved how many digits of pi you'd have to store to do that (though I can't say I understood the proof) -- I think you needed all those digits of pi because the trig functions are defined on the range 0 -- pi/2, and any larger value needs to be mapped to that domain -- if someone asks for the sin(e100), you need to know pretty exactly what x % pi/4 is. The problem with trig functions is that they suffer from "the > table maker's dilemma", so it is very hard to guarantee a correctly > rounded result without going to ludicrous extremes: > > http://perso.ens-lyon.fr/jean-michel.muller/Intro-to-TMD.htm > > So I think that there's no guarantee given for trancendental functions > like sine, cosine etc. > so -- if that's the case, I still think we know that while the last digit may not be the best rounded value to the real one, the second to last digit is correct. And if not, then there's nothing we could do other than implement the math lib :-) But even if they were, using isclose() is the wrong solution. Suppose > sin(x) returns some number y, such that isclose(y, 0.0) say. You have no > way of knowing that y is an inaccurate result that ought to be zero, or > whether the answer should be non-zero and y is correct. You cannot > assume that "y is close to zero, therefore it ought to be zero". > no, but you can say "y is as close to zero as I care about" we are already restricted to not knowing the distinction within less than an eps -- so making the "effective eps" a bit larger would result in more esthetically pleasing results. It's not just zero, the same applies for any value. That's just moving > rounding errors from one input to a slightly different input. > > # current situation > sine of x returns y, but the mathematical exact result is exactly z > > # suggested "fix" > sine of x ± a tiny bit returns exactly z, but ought to return y > > Guessing what sin or cos "ought to" return based on either the inexact > input or inexact output is not a good approach. > I don't think that's what it would be -- rather, it would be returning a bit less precision in exchange for more esthetically pleasing results :-) Note that there is no way I would advocate using this for the stdlib trig functions -- only for a purpose library. I'm also suggesting that it would result in equally good results to what is being proposed: using integer degrees, or a pi or tau based units. If you used integer degrees, then you'd have exactly, say pi (180 degrees), but a precision of only pi/180 -- much less than the 15 digits or so you'd get if you rounded the regular floating point results. And if you used tau based units, you'd be back to the same thing -- 0.5 tau could be exact, but what would you do for a bit bigger or smaller than that? use FP :-) > We can only operate on multiples of pi, > which is *close to* but not the same as π. That's why it is okay that > tan(pi/2) returns a huge number instead of infinity or NAN. That's > because the input is every so slightly smaller than π/2. That's exactly > the behavior you want when x is ever so slightly smaller than π/2. > I suppose so, but is there a guarantee that the FP representation of π/2 is a tiny bit less than the exact value, rather than a tiny bit more? which would result in VERY different answers: In [*10*]: math.tan(math.pi / 2.0) Out[*10*]: 1.633123935319537e+16 In [*11*]: math.tan(math.pi / 2.0 + 2e-16) Out[*11*]: -6218431163823738.0 (though equally "correct") Also -- 1.6 e+16 is actually pretty darn small compared to FP range. So a library that wants to produce "expected" results may want to do something with that -- something like: In [*119*]: *def* pretty_tan(x): ...: tol = 3e-16 ...: diff = x % (pi / 2) ...: *if* abs(diff) < tol: ...: *return* float("-Inf") ...: *elif* (pi /2) - diff < tol: ...: *return* float("Inf") ...: *return* math.tan(x) ...: ...: In [*120*]: x = pi / 2 - 5e-16 In [*121*]: *for* i *in* range(10): ...: val = x + i * 1e-16 ...: *print* val, pretty_tan(val) ...: 1.57079632679 1.9789379661e+15 1.57079632679 1.9789379661e+15 1.57079632679 inf 1.57079632679 inf 1.57079632679 -inf 1.57079632679 -inf 1.57079632679 -inf 1.57079632679 -inf 1.57079632679 -2.61194216074e+15 1.57079632679 -2.61194216074e+15 You'd want to tweak that tolerance value to be as small as possible, and do somethign to make it more symmetric, but you get the idea. The goal is that if you have an input that is about as close as you can get to pi/2, you get inf or -inf as a result. This does mean you are tossing away a tiny bit of precision -- you could get a "correct" value for those values really close to pi/2, but it would be prettier... -CHB > > > -- > Steve > _______________________________________________ > Python-ideas mailing list > Python-ideas@python.org > https://mail.python.org/mailman/listinfo/python-ideas > Code of Conduct: http://python.org/psf/codeofconduct/ > -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception chris.bar...@noaa.gov
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/