[Python-ideas] Fwd: Trigonometry in degrees
chris.barker at noaa.gov
Mon Jun 11 17:12:06 EDT 2018
On Sun, Jun 10, 2018 at 10:48 PM, Steven D'Aprano <steve at pearwood.info>
> > 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()
> > 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:
> 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
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)
In [*11*]: math.tan(math.pi / 2.0 + 2e-16)
(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)
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
> Python-ideas mailing list
> Python-ideas at python.org
> Code of Conduct: http://python.org/psf/codeofconduct/
Christopher Barker, Ph.D.
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.Barker at noaa.gov
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Python-ideas