[Python-ideas] Fwd: Trigonometry in degrees

Tim Peters tim.peters at gmail.com
Tue Jun 12 01:50:56 EDT 2018


[Steven D'Aprano]

> ...

The initial proposal is fine: a separate set of trig functions that take
> their arguments in degrees would have no unexpected surprises (only the
> expected ones). With a decent implementation i.e. not this one:
>
>     # don't do this
>     def sind(angle):
>         return math.sin(math.radians(angle))
>

But that's good enough for almost all real purposes.  Indeed, except for
argument reduction, it's essentially how scipy's sindg and cosdg _are_
implemented:

https://github.com/scipy/scipy/blob/master/scipy/special/cephes/sindg.c


> and equivalent for cos, we ought to be able to get correctly rounded
> values for nearly all the "interesting" angles of the circle, without
> those pesky rounding issues caused by π not being exactly representable
> as a float.
>
> If people are overly ;-) worried about tiny rounding errors, just compute
things with some extra bits of precision to absorb them.  For example,
install `mpmath` and use this:

     def sindg(d):
        import math, mpmath
        d = math.fmod(d, 360.0)
        if abs(d) == 180.0:
            return 0.0
        with mpmath.extraprec(12):
            return float(mpmath.sin(mpmath.radians(d)))

Then, e.g,

>>> for x in (0, 30, 90, 150, 180, 210, 270, 330, 360):
...     print(x, sindg(x))
0 0.0
30 0.5
90 1.0
150 0.5
180 0.0
210 -0.5
270 -1.0
330 -0.5
360 0.0

Notes:

1. Python's float "%" is unsuitable for argument reduction; e.g.,

>>> -1e-14 % 360.0
360.0

`math.fmod` is suitable, because it's exact:

>>> math.fmod(-1e-14, 360.0)
-1e-14

2. Using a dozen extra bits of precision make it very likely you'll get the
correctly rounded 53-bit result; it will almost certainly (barring bugs in
`mpmath`) always be good to less than 1 ULP.

3. Except for +-180.  No matter how many bits of float precision (including
the number of bits used to approximate pi) are used, converting that to
radians can never yield the mathematical `pi`; and sin(pi+x) is
approximately equal to -x for tiny |x|; e.g., here with a thousand bits:

>>> mpmath.mp.prec = 1000
>>> float(mpmath.sin(mpmath.radians(180)))
1.2515440597544546e-301

So +-180 is special-cased.  For cosdg, +-{90. 270} would need to be
special-cased for the same reason.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20180612/3dc67858/attachment-0001.html>


More information about the Python-ideas mailing list