Skip to content

Commit e4cf911

Browse files
pkofodararslan
authored andcommitted
Add Julia port of atan from Openlibm. (#23383)
* Add Julia port of atan from Openlibm. * Add tests. * Remove double fastmath definition. * Fix HI->LO in branch in atan. * Remove explicit nan check. * fix indentation in branch. * Add mention of origins of code. * Fix tests.
1 parent f2d41b8 commit e4cf911

File tree

4 files changed

+133
-3
lines changed

4 files changed

+133
-3
lines changed

base/fastmath.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ sqrt_fast(x::FloatTypes) = sqrt_llvm(x)
275275

276276
const libm = Base.libm_name
277277

278-
for f in (:acosh, :asinh, :atan, :atanh, :cbrt, :cos,
278+
for f in (:acosh, :asinh, :atanh, :cbrt, :cos,
279279
:cosh, :exp2, :expm1, :lgamma, :log10, :log1p, :log2,
280280
:log, :sin, :sinh, :tan, :tanh)
281281
f_fast = fast_op[f]

base/math.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ asinh(x::Number)
244244
Accurately compute ``e^x-1``.
245245
"""
246246
expm1(x)
247-
for f in (:cbrt, :sinh, :cosh, :tanh, :atan, :asinh, :exp2, :expm1)
247+
for f in (:cbrt, :sinh, :cosh, :tanh, :asinh, :exp2, :expm1)
248248
@eval begin
249249
($f)(x::Float64) = ccall(($(string(f)),libm), Float64, (Float64,), x)
250250
($f)(x::Float32) = ccall(($(string(f,"f")),libm), Float32, (Float32,), x)
@@ -253,7 +253,7 @@ for f in (:cbrt, :sinh, :cosh, :tanh, :atan, :asinh, :exp2, :expm1)
253253
end
254254
exp(x::Real) = exp(float(x))
255255
exp10(x::Real) = exp10(float(x))
256-
256+
atan(x::Real) = atan(float(x))
257257
# fallback definitions to prevent infinite loop from $f(x::Real) def above
258258

259259
"""

base/special/trig.jl

+103
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ end
1111

1212
# sin_kernel and cos_kernel functions are only valid for |x| < pi/4 = 0.7854
1313
# translated from openlibm code: k_sin.c, k_cos.c, k_sinf.c, k_cosf.c.
14+
# atan functions are based on openlibm code: s_atan.c, s_atanf.c.
1415
# acos functions are based on openlibm code: e_acos.c, e_acosf.c.
1516
# asin functions are based on openlibm code: e_asin.c, e_asinf.c. The above
1617
# functions are made available under the following licence:
@@ -444,6 +445,108 @@ function asin(x::T) where T<:Union{Float32, Float64}
444445
end
445446
asin(x::Real) = asin(float(x))
446447

448+
# atan methods
449+
ATAN_1_O_2_HI(::Type{Float64}) = 4.63647609000806093515e-01 # atan(0.5).hi
450+
ATAN_2_O_2_HI(::Type{Float64}) = 7.85398163397448278999e-01 # atan(1.0).hi
451+
ATAN_3_O_2_HI(::Type{Float64}) = 9.82793723247329054082e-01 # atan(1.5).hi
452+
ATAN_INF_HI(::Type{Float64}) = 1.57079632679489655800e+00 # atan(Inf).hi
453+
454+
ATAN_1_O_2_HI(::Type{Float32}) = 4.6364760399f-01 # atan(0.5).hi
455+
ATAN_2_O_2_HI(::Type{Float32}) = 7.8539812565f-01 # atan(1.0).hi
456+
ATAN_3_O_2_HI(::Type{Float32}) = 9.8279368877f-01 # atan(1.5).hi
457+
ATAN_INF_HI(::Type{Float32}) = 1.5707962513f+00 # atan(Inf).hi
458+
459+
ATAN_1_O_2_LO(::Type{Float64}) = 2.26987774529616870924e-17 # atan(0.5).lo
460+
ATAN_2_O_2_LO(::Type{Float64}) = 3.06161699786838301793e-17 # atan(1.0).lo
461+
ATAN_3_O_2_LO(::Type{Float64}) = 1.39033110312309984516e-17 # atan(1.5).lo
462+
ATAN_INF_LO(::Type{Float64}) = 6.12323399573676603587e-17 # atan(Inf).lo
463+
464+
ATAN_1_O_2_LO(::Type{Float32}) = 5.0121582440f-09 # atan(0.5).lo
465+
ATAN_2_O_2_LO(::Type{Float32}) = 3.7748947079f-08 # atan(1.0).lo
466+
ATAN_3_O_2_LO(::Type{Float32}) = 3.4473217170f-08 # atan(1.5).lo
467+
ATAN_INF_LO(::Type{Float32}) = 7.5497894159f-08 # atan(Inf).lo
468+
469+
ATAN_LARGE_X(::Type{Float64}) = 2.0^66 # seems too large? 2.0^60 gives the same
470+
ATAN_SMALL_X(::Type{Float64}) = 2.0^-27
471+
ATAN_LARGE_X(::Type{Float32}) = 2.0f0^26
472+
ATAN_SMALL_X(::Type{Float32}) = 2.0f0^-12
473+
474+
atan_p(z::Float64, w::Float64) = z*@horner(w,
475+
3.33333333333329318027e-01,
476+
1.42857142725034663711e-01,
477+
9.09088713343650656196e-02,
478+
6.66107313738753120669e-02,
479+
4.97687799461593236017e-02,
480+
1.62858201153657823623e-02)
481+
atan_q(w::Float64) = w*@horner(w,
482+
-1.99999999998764832476e-01,
483+
-1.11111104054623557880e-01,
484+
-7.69187620504482999495e-02,
485+
-5.83357013379057348645e-02,
486+
-3.65315727442169155270e-02)
487+
atan_p(z::Float32, w::Float32) = z*@horner(w, 3.3333328366f-01, 1.4253635705f-01, 6.1687607318f-02)
488+
atan_q(w::Float32) = w*@horner(w, -1.9999158382f-01, -1.0648017377f-01)
489+
@inline function atan_pq(x)
490+
= x*x
491+
x⁴ =*
492+
# break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly
493+
atan_p(x², x⁴), atan_q(x⁴)
494+
end
495+
function atan(x::T) where T<:Union{Float32, Float64}
496+
# Method
497+
# 1. Reduce x to positive by atan(x) = -atan(-x).
498+
# 2. According to the integer k=4t+0.25 chopped, t=x, the argument
499+
# is further reduced to one of the following intervals and the
500+
# arctangent of t is evaluated by the corresponding formula:
501+
#
502+
# [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...)
503+
# [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) )
504+
# [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) )
505+
# [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) )
506+
# [39/16,INF] atan(x) = atan(INF) + atan( -1/t )
507+
#
508+
# If isnan(x) is true, then the nan value will eventually be passed to
509+
# atan_pq(x) and return the appropriate nan value.
510+
511+
absx = abs(x)
512+
if absx >= ATAN_LARGE_X(T)
513+
return copysign(T(1.5707963267948966), x)
514+
end
515+
if absx < T(7/16)
516+
# no reduction needed
517+
if absx < ATAN_SMALL_X(T)
518+
return x
519+
end
520+
p, q = atan_pq(x)
521+
return x - x*(p + q)
522+
end
523+
xsign = sign(x)
524+
if absx < T(19/16) # 7/16 <= |x| < 19/16
525+
if absx < T(11/16) # 7/16 <= |x| <11/16
526+
hi = ATAN_1_O_2_HI(T)
527+
lo = ATAN_1_O_2_LO(T)
528+
x = (T(2.0)*absx - T(1.0))/(T(2.0) + absx)
529+
else # 11/16 <= |x| < 19/16
530+
hi = ATAN_2_O_2_HI(T)
531+
lo = ATAN_2_O_2_LO(T)
532+
x = (absx - T(1.0))/(absx + T(1.0))
533+
end
534+
else
535+
if absx < T(39/16) # 19/16 <= |x| < 39/16
536+
hi = ATAN_3_O_2_HI(T)
537+
lo = ATAN_3_O_2_LO(T)
538+
x = (absx - T(1.5))/(T(1.0) + T(1.5)*absx)
539+
else # 39/16 <= |x| < upper threshold (2.0^66 or 2.0f0^26)
540+
hi = ATAN_INF_HI(T)
541+
lo = ATAN_INF_LO(T)
542+
x = -T(1.0)/absx
543+
end
544+
end
545+
# end of argument reduction
546+
p, q = atan_pq(x)
547+
z = hi - ((x*(p + q) - lo) - x)
548+
copysign(z, xsign)
549+
end
447550
# atan2 methods
448551
ATAN2_PI_LO(::Type{Float32}) = -8.7422776573f-08
449552
ATAN2_RATIO_BIT_SHIFT(::Type{Float32}) = 23

test/math.jl

+27
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,33 @@ end
756756
end
757757
end
758758

759+
@testset "atan #23383" begin
760+
for T in (Float32, Float64)
761+
@test atan(T(NaN)) === T(NaN)
762+
@test atan(-T(Inf)) === -T(pi)/2
763+
@test atan(T(Inf)) === T(pi)/2
764+
# no reduction needed |x| < 7/16
765+
@test atan(zero(T)) === zero(T)
766+
@test atan(prevfloat(zero(T))) === prevfloat(zero(T))
767+
@test atan(nextfloat(zero(T))) === nextfloat(zero(T))
768+
for x in (T(7/16), (T(7/16)+T(11/16))/2, T(11/16),
769+
(T(11/16)+T(19/16))/2, T(19/16),
770+
(T(19/16)+T(39/16))/2, T(39/16),
771+
(T(39/16)+T(2)^23)/2, T(2)^23)
772+
x = T(7/16)
773+
by = T(atan(big(x)))
774+
@test abs(atan(x) - by)/eps(by) <= one(T)
775+
x = prevfloat(T(7/16))
776+
by = T(atan(big(x)))
777+
@test abs(atan(x) - by)/eps(by) <= one(T)
778+
x = nextfloat(T(7/16))
779+
by = T(atan(big(x)))
780+
@test abs(atan(x) - by)/eps(by) <= one(T)
781+
end
782+
# This case was used to find a bug, but it isn't special in itself
783+
@test atan(1.7581305072934137) 1.053644580517088
784+
end
785+
end
759786
@testset "atan2" begin
760787
for T in (Float32, Float64)
761788
@test isnan_type(T, atan2(T(NaN), T(NaN)))

0 commit comments

Comments
 (0)