2,906 bytes · the Python implementation · view raw
# Derived from fdlibm s_atan.c.# Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.# Developed at SunSoft, a Sun Microsystems, Inc. business.# Permission to use, copy, modify, and distribute this# software is freely granted, provided that this notice# is preserved.import math
# fdlibm s_atan.c (Sun Microsystems, 1993). atan of 0.5, 1, 1.5 and infinity,# each split into a high and a low part so the sum carries more than 53 bits.
ATAN_HI = (
4.63647609000806093515e-01,
7.85398163397448278999e-01,
9.82793723247329054082e-01,
1.57079632679489655800e00,
)
ATAN_LO = (
2.26987774529616870924e-17,
3.06161699786838301793e-17,
1.39033110312309984516e-17,
6.12323399573676603587e-17,
)
AT = (
3.33333333333329318027e-01,
-1.99999999998764832476e-01,
1.42857142725034663711e-01,
-1.11111104054623557880e-01,
9.09088713343650656196e-02,
-7.69187620504482999495e-02,
6.66107313738753120669e-02,
-5.83357013379057348645e-02,
4.97687799461593236017e-02,
-3.65315727442169155270e-02,
1.62858201153657823623e-02,
)
TWO_POW_66 = 73786976294838206464.0
TWO_POW_M29 = 1.862645149230957e-09def atan(x: float) -> float:
"""Arctangent in radians using only +, -, * and /, in the same order as the TypeScript and Rust versions, so all three return the same double. math.atan comes from the C library and may differ in the last bit. fdlibm's method: reduce |x| against atan(0.5), atan(1), atan(1.5) or pi/2, then an odd polynomial on the remainder. fdlibm picks the interval from the high word of the double; comparing |x| with the same boundaries (7/16, 11/16, 19/16, 39/16, 2^66, 2^-29) is exactly equivalent."""if isinstance(x, bool) ornot isinstance(x, (int, float)) ornot math.isfinite(x):
raise ValueError("x must be a finite number, received %r" % (x,))
x = float(x)
negative = x < 0
a = -x if negative else x
if a >= TWO_POW_66:
z = ATAN_HI[3] + ATAN_LO[3]
return -z if negative else z
if a < 0.4375:
if a < TWO_POW_M29:
# atan(x) rounds to x here; + 0.0 turns -0 into 0.return x + 0.0
i = -1else:
x = a
if a < 1.1875:
if a < 0.6875:
i = 0
x = (2.0 * x - 1.0) / (2.0 + x)
else:
i = 1
x = (x - 1.0) / (x + 1.0)
elif a < 2.4375:
i = 2
x = (x - 1.5) / (1.0 + 1.5 * x)
else:
i = 3
x = -1.0 / x
z = x * x
w = z * z
s1 = z * (AT[0] + w * (AT[2] + w * (AT[4] + w * (AT[6] + w * (AT[8] + w * AT[10])))))
s2 = w * (AT[1] + w * (AT[3] + w * (AT[5] + w * (AT[7] + w * AT[9]))))
if i < 0:
return x - x * (s1 + s2)
z = ATAN_HI[i] - ((x * (s1 + s2) - ATAN_LO[i]) - x)
return -z if negative else z