Skip to content

Commit 8e4b22d

Browse files
committed
add sincospi (JuliaLang#35816)
1 parent c14693e commit 8e4b22d

File tree

4 files changed

+151
-19
lines changed

4 files changed

+151
-19
lines changed

base/exports.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ export
329329
sinc,
330330
sincos,
331331
sincosd,
332+
sincospi,
332333
sind,
333334
sinh,
334335
sinpi,

base/math.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Math
55
export sin, cos, sincos, tan, sinh, cosh, tanh, asin, acos, atan,
66
asinh, acosh, atanh, sec, csc, cot, asec, acsc, acot,
77
sech, csch, coth, asech, acsch, acoth,
8-
sinpi, cospi, sinc, cosc,
8+
sinpi, cospi, sincospi, sinc, cosc,
99
cosd, cotd, cscd, secd, sind, tand, sincosd,
1010
acosd, acotd, acscd, asecd, asind, atand,
1111
rad2deg, deg2rad,

base/special/trig.jl

Lines changed: 124 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -777,8 +777,8 @@ function sinpi(x::T) where T<:AbstractFloat
777777
end
778778
end
779779

780-
# Integers and Rationals
781-
function sinpi(x::T) where T<:Union{Integer,Rational}
780+
# Rationals
781+
function sinpi(x::T) where T<:Rational
782782
Tf = float(T)
783783
if !isfinite(x)
784784
throw(DomainError(x, "`x` must be finite."))
@@ -836,8 +836,8 @@ function cospi(x::T) where T<:AbstractFloat
836836
end
837837
end
838838

839-
# Integers and Rationals
840-
function cospi(x::T) where T<:Union{Integer,Rational}
839+
# Rationals
840+
function cospi(x::T) where T<:Rational
841841
if !isfinite(x)
842842
throw(DomainError(x, "`x` must be finite."))
843843
end
@@ -860,10 +860,79 @@ function cospi(x::T) where T<:Union{Integer,Rational}
860860
end
861861
end
862862

863+
"""
864+
sincospi(x)
865+
866+
Simultaneously compute `sinpi(x)` and `cospi(x)`, where the `x` is in radians.
867+
"""
868+
function sincospi(x::T) where T<:AbstractFloat
869+
if !isfinite(x)
870+
isnan(x) && return x, x
871+
throw(DomainError(x, "`x` cannot be infinite."))
872+
end
873+
874+
ax = abs(x)
875+
s = maxintfloat(T)
876+
ax >= s && return (copysign(zero(T), x), one(T)) # even integer-valued
877+
878+
# reduce to interval [-1,1]
879+
# assumes RoundNearest rounding mode
880+
t = 3*(s/2)
881+
rx = x-((x+t)-t) # zeros may be incorrectly signed
882+
arx = abs(rx)
883+
884+
# same selection scheme as sinpi and cospi
885+
if (arx == 0) | (arx == 1)
886+
return copysign(zero(T), x), ifelse(ax % 2 == 0, one(T), -one(T))
887+
elseif arx < 0.25
888+
return sincos_kernel(mulpi_ext(rx))
889+
elseif arx < 0.75
890+
y = mulpi_ext(T(0.5) - arx)
891+
return copysign(cos_kernel(y), rx), sin_kernel(y)
892+
else
893+
y_si = mulpi_ext(copysign(one(T), rx) - rx)
894+
y_co = mulpi_ext(one(T) - arx)
895+
return sin_kernel(y_si), -cos_kernel(y_co)
896+
end
897+
end
898+
899+
# Rationals
900+
function sincospi(x::T) where T<:Rational
901+
Tf = float(T)
902+
if !isfinite(x)
903+
throw(DomainError(x, "`x` must be finite."))
904+
end
905+
906+
# until we get an IEEE remainder function (#9283)
907+
rx = rem(x,2)
908+
if rx > 1
909+
rx -= 2
910+
elseif rx < -1
911+
rx += 2
912+
end
913+
arx = abs(rx)
914+
915+
# same selection scheme as sinpi and cospi
916+
if (arx == 0) | (arx == 1)
917+
return copysign(zero(Tf),x), ifelse(iseven(numerator(x)), one(Tf), -one(Tf))
918+
elseif arx < 0.25
919+
return sincos_kernel(mulpi_ext(rx))
920+
elseif arx < 0.75
921+
y = mulpi_ext(T(0.5) - arx)
922+
return copysign(cos_kernel(y), rx), sin_kernel(y)
923+
else
924+
y_si = mulpi_ext(copysign(one(T), rx) - rx)
925+
y_co = mulpi_ext(one(T) - arx)
926+
return sin_kernel(y_si), -cos_kernel(y_co)
927+
end
928+
end
929+
863930
sinpi(x::T) where {T<:Integer} = zero(signed(T))
864931
cospi(x::T) where {T<:Integer} = isodd(x) ? -one(signed(T)) : one(signed(T))
932+
sincospi(x::Integer) = (sinpi(x), cospi(x))
865933
sinpi(x::Real) = sinpi(float(x))
866934
cospi(x::Real) = cospi(float(x))
935+
sincospi(x::Real) = sincospi(float(x))
867936

868937
function sinpi(z::Complex{T}) where T
869938
F = float(T)
@@ -893,7 +962,8 @@ function sinpi(z::Complex{T}) where T
893962
end
894963
else
895964
pizi = pi*zi
896-
Complex(sinpi(zr)*cosh(pizi), cospi(zr)*sinh(pizi))
965+
sipi, copi = sincospi(zr)
966+
Complex(sipi*cosh(pizi), copi*sinh(pizi))
897967
end
898968
end
899969

@@ -928,7 +998,55 @@ function cospi(z::Complex{T}) where T
928998
end
929999
else
9301000
pizi = pi*zi
931-
Complex(cospi(zr)*cosh(pizi), -sinpi(zr)*sinh(pizi))
1001+
sipi, copi = sincospi(zr)
1002+
Complex(copi*cosh(pizi), -sipi*sinh(pizi))
1003+
end
1004+
end
1005+
1006+
function sincospi(z::Complex{T}) where T
1007+
F = float(T)
1008+
zr, zi = reim(z)
1009+
if isinteger(zr)
1010+
# zr = ...,-2,-1,0,1,2,...
1011+
# sin(pi*zr) == ±0
1012+
# cos(pi*zr) == ±1
1013+
# cosh(pi*zi) > 0
1014+
s = copysign(zero(F),zr)
1015+
c_pos = isa(zr,Integer) ? iseven(zr) : isinteger(zr/2)
1016+
pizi = pi*zi
1017+
sh, ch = sinh(pizi), cosh(pizi)
1018+
(
1019+
Complex(s, c_pos ? sh : -sh),
1020+
Complex(c_pos ? ch : -ch, isnan(zi) ? s : -flipsign(s,zi)),
1021+
)
1022+
elseif isinteger(2*zr)
1023+
# zr = ...,-1.5,-0.5,0.5,1.5,2.5,...
1024+
# sin(pi*zr) == ±1
1025+
# cos(pi*zr) == +0
1026+
# sign(sinh(pi*zi)) == sign(zi)
1027+
s_pos = isinteger((2*zr-1)/4)
1028+
pizi = pi*zi
1029+
sh, ch = sinh(pizi), cosh(pizi)
1030+
(
1031+
Complex(s_pos ? ch : -ch, isnan(zi) ? zero(F) : copysign(zero(F),zi)),
1032+
Complex(zero(F), s_pos ? -sh : sh),
1033+
)
1034+
elseif !isfinite(zr)
1035+
if zi == 0
1036+
Complex(F(NaN), F(zi)), Complex(F(NaN), isnan(zr) ? zero(F) : -flipsign(F(zi),zr))
1037+
elseif isinf(zi)
1038+
Complex(F(NaN), F(zi)), Complex(F(Inf), F(NaN))
1039+
else
1040+
Complex(F(NaN), F(NaN)), Complex(F(NaN), F(NaN))
1041+
end
1042+
else
1043+
pizi = pi*zi
1044+
sipi, copi = sincospi(zr)
1045+
sihpi, cohpi = sinh(pizi), cosh(pizi)
1046+
(
1047+
Complex(sipi*cohpi, copi*sihpi),
1048+
Complex(copi*cohpi, -sipi*sihpi),
1049+
)
9321050
end
9331051
end
9341052

test/math.jl

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -408,8 +408,11 @@ end
408408
@test sincosd(convert(T, 270))::fTsc === ( -one(fT), zero(fT) )
409409
end
410410

411-
@testset "sinpi and cospi" begin
412-
for x = -3:0.3:3
411+
@testset "$name" for (name, (sinpi, cospi)) in (
412+
"sinpi and cospi" => (sinpi, cospi),
413+
"sincospi" => (x->sincospi(x)[1], x->sincospi(x)[2])
414+
)
415+
@testset "pi * $x" for x = -3:0.3:3
413416
@test sinpi(convert(T,x))::fT convert(fT,sin(pi*x)) atol=eps(pi*convert(fT,x))
414417
@test cospi(convert(T,x))::fT convert(fT,cos(pi*x)) atol=eps(pi*convert(fT,x))
415418
end
@@ -433,10 +436,13 @@ end
433436
@test cosd(convert(T,60)) == 0.5
434437
@test sind(convert(T,150)) == 0.5
435438
@test sinpi(one(T)/convert(T,6)) == 0.5
439+
@test sincospi(one(T)/convert(T,6))[1] == 0.5
436440
@test_throws DomainError sind(convert(T,Inf))
437441
@test_throws DomainError cosd(convert(T,Inf))
438442
T != Float32 && @test cospi(one(T)/convert(T,3)) == 0.5
443+
T != Float32 && @test sincospi(one(T)/convert(T,3))[2] == 0.5
439444
T == Rational{Int} && @test sinpi(5//6) == 0.5
445+
T == Rational{Int} && @test sincospi(5//6)[1] == 0.5
440446
end
441447
end
442448
scdm = sincosd(missing)
@@ -445,10 +451,12 @@ end
445451
end
446452

447453
@testset "Integer args to sinpi/cospi/sinc/cosc" begin
448-
@test sinpi(1) == 0
449-
@test sinpi(-1) == -0
450-
@test cospi(1) == -1
451-
@test cospi(2) == 1
454+
for (sinpi, cospi) in ((sinpi, cospi), (x->sincospi(x)[1], x->sincospi(x)[2]))
455+
@test sinpi(1) == 0
456+
@test sinpi(-1) == -0
457+
@test cospi(1) == -1
458+
@test cospi(2) == 1
459+
end
452460

453461
@test sinc(1) == 0
454462
@test sinc(complex(1,0)) == 0
@@ -462,20 +470,25 @@ end
462470

463471
@testset "Irrational args to sinpi/cospi/sinc/cosc" begin
464472
for x in (pi, ℯ, Base.MathConstants.golden)
465-
@test sinpi(x) Float64(sinpi(big(x)))
466-
@test cospi(x) Float64(cospi(big(x)))
473+
for (sinpi, cospi) in ((sinpi, cospi), (x->sincospi(x)[1], x->sincospi(x)[2]))
474+
@test sinpi(x) Float64(sinpi(big(x)))
475+
@test cospi(x) Float64(cospi(big(x)))
476+
@test sinpi(complex(x, x)) Complex{Float64}(sinpi(complex(big(x), big(x))))
477+
@test cospi(complex(x, x)) Complex{Float64}(cospi(complex(big(x), big(x))))
478+
end
467479
@test sinc(x) Float64(sinc(big(x)))
468480
@test cosc(x) Float64(cosc(big(x)))
469-
@test sinpi(complex(x, x)) Complex{Float64}(sinpi(complex(big(x), big(x))))
470-
@test cospi(complex(x, x)) Complex{Float64}(cospi(complex(big(x), big(x))))
471481
@test sinc(complex(x, x)) Complex{Float64}(sinc(complex(big(x), big(x))))
472482
@test cosc(complex(x, x)) Complex{Float64}(cosc(complex(big(x), big(x))))
473483
end
474484
end
475485

476486
@testset "trig function type stability" begin
477-
@testset "$T $f" for T = (Float32,Float64,BigFloat), f = (sind,cosd,sinpi,cospi)
478-
@test Base.return_types(f,Tuple{T}) == [T]
487+
@testset "$T $f" for T = (Float32,Float64,BigFloat,Rational{Int16},Complex{Int32},ComplexF16), f = (sind,cosd,sinpi,cospi)
488+
@test Base.return_types(f,Tuple{T}) == [float(T)]
489+
end
490+
@testset "$T sincospi" for T = (Float32,Float64,BigFloat,Rational{Int16},Complex{Int32},ComplexF16)
491+
@test Base.return_types(sincospi,Tuple{T}) == [Tuple{float(T),float(T)}]
479492
end
480493
end
481494

0 commit comments

Comments
 (0)