Skip to content

Commit

Permalink
add sincospi (#35816)
Browse files Browse the repository at this point in the history
  • Loading branch information
simeonschaub authored Jun 23, 2020
1 parent 9b2c6b2 commit 6cd329c
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 19 deletions.
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ export
sinc,
sincos,
sincosd,
sincospi,
sind,
sinh,
sinpi,
Expand Down
2 changes: 1 addition & 1 deletion base/math.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Math
export sin, cos, sincos, tan, sinh, cosh, tanh, asin, acos, atan,
asinh, acosh, atanh, sec, csc, cot, asec, acsc, acot,
sech, csch, coth, asech, acsch, acoth,
sinpi, cospi, sinc, cosc,
sinpi, cospi, sincospi, sinc, cosc,
cosd, cotd, cscd, secd, sind, tand, sincosd,
acosd, acotd, acscd, asecd, asind, atand,
rad2deg, deg2rad,
Expand Down
130 changes: 124 additions & 6 deletions base/special/trig.jl
Original file line number Diff line number Diff line change
Expand Up @@ -777,8 +777,8 @@ function sinpi(x::T) where T<:AbstractFloat
end
end

# Integers and Rationals
function sinpi(x::T) where T<:Union{Integer,Rational}
# Rationals
function sinpi(x::T) where T<:Rational
Tf = float(T)
if !isfinite(x)
throw(DomainError(x, "`x` must be finite."))
Expand Down Expand Up @@ -836,8 +836,8 @@ function cospi(x::T) where T<:AbstractFloat
end
end

# Integers and Rationals
function cospi(x::T) where T<:Union{Integer,Rational}
# Rationals
function cospi(x::T) where T<:Rational
if !isfinite(x)
throw(DomainError(x, "`x` must be finite."))
end
Expand All @@ -860,10 +860,79 @@ function cospi(x::T) where T<:Union{Integer,Rational}
end
end

"""
sincospi(x)
Simultaneously compute `sinpi(x)` and `cospi(x)`, where the `x` is in radians.
"""
function sincospi(x::T) where T<:AbstractFloat
if !isfinite(x)
isnan(x) && return x, x
throw(DomainError(x, "`x` cannot be infinite."))
end

ax = abs(x)
s = maxintfloat(T)
ax >= s && return (copysign(zero(T), x), one(T)) # even integer-valued

# reduce to interval [-1,1]
# assumes RoundNearest rounding mode
t = 3*(s/2)
rx = x-((x+t)-t) # zeros may be incorrectly signed
arx = abs(rx)

# same selection scheme as sinpi and cospi
if (arx == 0) | (arx == 1)
return copysign(zero(T), x), ifelse(ax % 2 == 0, one(T), -one(T))
elseif arx < 0.25
return sincos_kernel(mulpi_ext(rx))
elseif arx < 0.75
y = mulpi_ext(T(0.5) - arx)
return copysign(cos_kernel(y), rx), sin_kernel(y)
else
y_si = mulpi_ext(copysign(one(T), rx) - rx)
y_co = mulpi_ext(one(T) - arx)
return sin_kernel(y_si), -cos_kernel(y_co)
end
end

# Rationals
function sincospi(x::T) where T<:Rational
Tf = float(T)
if !isfinite(x)
throw(DomainError(x, "`x` must be finite."))
end

# until we get an IEEE remainder function (#9283)
rx = rem(x,2)
if rx > 1
rx -= 2
elseif rx < -1
rx += 2
end
arx = abs(rx)

# same selection scheme as sinpi and cospi
if (arx == 0) | (arx == 1)
return copysign(zero(Tf),x), ifelse(iseven(numerator(x)), one(Tf), -one(Tf))
elseif arx < 0.25
return sincos_kernel(mulpi_ext(rx))
elseif arx < 0.75
y = mulpi_ext(T(0.5) - arx)
return copysign(cos_kernel(y), rx), sin_kernel(y)
else
y_si = mulpi_ext(copysign(one(T), rx) - rx)
y_co = mulpi_ext(one(T) - arx)
return sin_kernel(y_si), -cos_kernel(y_co)
end
end

sinpi(x::Integer) = x >= 0 ? zero(float(x)) : -zero(float(x))
cospi(x::Integer) = isodd(x) ? -one(float(x)) : one(float(x))
sincospi(x::Integer) = (sinpi(x), cospi(x))
sinpi(x::Real) = sinpi(float(x))
cospi(x::Real) = cospi(float(x))
sincospi(x::Real) = sincospi(float(x))

function sinpi(z::Complex{T}) where T
F = float(T)
Expand Down Expand Up @@ -893,7 +962,8 @@ function sinpi(z::Complex{T}) where T
end
else
pizi = pi*zi
Complex(sinpi(zr)*cosh(pizi), cospi(zr)*sinh(pizi))
sipi, copi = sincospi(zr)
Complex(sipi*cosh(pizi), copi*sinh(pizi))
end
end

Expand Down Expand Up @@ -928,7 +998,55 @@ function cospi(z::Complex{T}) where T
end
else
pizi = pi*zi
Complex(cospi(zr)*cosh(pizi), -sinpi(zr)*sinh(pizi))
sipi, copi = sincospi(zr)
Complex(copi*cosh(pizi), -sipi*sinh(pizi))
end
end

function sincospi(z::Complex{T}) where T
F = float(T)
zr, zi = reim(z)
if isinteger(zr)
# zr = ...,-2,-1,0,1,2,...
# sin(pi*zr) == ±0
# cos(pi*zr) == ±1
# cosh(pi*zi) > 0
s = copysign(zero(F),zr)
c_pos = isa(zr,Integer) ? iseven(zr) : isinteger(zr/2)
pizi = pi*zi
sh, ch = sinh(pizi), cosh(pizi)
(
Complex(s, c_pos ? sh : -sh),
Complex(c_pos ? ch : -ch, isnan(zi) ? s : -flipsign(s,zi)),
)
elseif isinteger(2*zr)
# zr = ...,-1.5,-0.5,0.5,1.5,2.5,...
# sin(pi*zr) == ±1
# cos(pi*zr) == +0
# sign(sinh(pi*zi)) == sign(zi)
s_pos = isinteger((2*zr-1)/4)
pizi = pi*zi
sh, ch = sinh(pizi), cosh(pizi)
(
Complex(s_pos ? ch : -ch, isnan(zi) ? zero(F) : copysign(zero(F),zi)),
Complex(zero(F), s_pos ? -sh : sh),
)
elseif !isfinite(zr)
if zi == 0
Complex(F(NaN), F(zi)), Complex(F(NaN), isnan(zr) ? zero(F) : -flipsign(F(zi),zr))
elseif isinf(zi)
Complex(F(NaN), F(zi)), Complex(F(Inf), F(NaN))
else
Complex(F(NaN), F(NaN)), Complex(F(NaN), F(NaN))
end
else
pizi = pi*zi
sipi, copi = sincospi(zr)
sihpi, cohpi = sinh(pizi), cosh(pizi)
(
Complex(sipi*cohpi, copi*sihpi),
Complex(copi*cohpi, -sipi*sihpi),
)
end
end

Expand Down
37 changes: 25 additions & 12 deletions test/math.jl
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,11 @@ end
@test sincosd(convert(T, 270))::fTsc === ( -one(fT), zero(fT) )
end

@testset "sinpi and cospi" begin
for x = -3:0.3:3
@testset "$name" for (name, (sinpi, cospi)) in (
"sinpi and cospi" => (sinpi, cospi),
"sincospi" => (x->sincospi(x)[1], x->sincospi(x)[2])
)
@testset "pi * $x" for x = -3:0.3:3
@test sinpi(convert(T,x))::fT convert(fT,sin(pi*x)) atol=eps(pi*convert(fT,x))
@test cospi(convert(T,x))::fT convert(fT,cos(pi*x)) atol=eps(pi*convert(fT,x))
end
Expand All @@ -433,10 +436,13 @@ end
@test cosd(convert(T,60)) == 0.5
@test sind(convert(T,150)) == 0.5
@test sinpi(one(T)/convert(T,6)) == 0.5
@test sincospi(one(T)/convert(T,6))[1] == 0.5
@test_throws DomainError sind(convert(T,Inf))
@test_throws DomainError cosd(convert(T,Inf))
T != Float32 && @test cospi(one(T)/convert(T,3)) == 0.5
T != Float32 && @test sincospi(one(T)/convert(T,3))[2] == 0.5
T == Rational{Int} && @test sinpi(5//6) == 0.5
T == Rational{Int} && @test sincospi(5//6)[1] == 0.5
end
end
scdm = sincosd(missing)
Expand All @@ -445,10 +451,12 @@ end
end

@testset "Integer args to sinpi/cospi/sinc/cosc" begin
@test sinpi(1) == 0
@test sinpi(-1) == -0
@test cospi(1) == -1
@test cospi(2) == 1
for (sinpi, cospi) in ((sinpi, cospi), (x->sincospi(x)[1], x->sincospi(x)[2]))
@test sinpi(1) == 0
@test sinpi(-1) == -0
@test cospi(1) == -1
@test cospi(2) == 1
end

@test sinc(1) == 0
@test sinc(complex(1,0)) == 0
Expand All @@ -462,20 +470,25 @@ end

@testset "Irrational args to sinpi/cospi/sinc/cosc" begin
for x in (pi, ℯ, Base.MathConstants.golden)
@test sinpi(x) Float64(sinpi(big(x)))
@test cospi(x) Float64(cospi(big(x)))
for (sinpi, cospi) in ((sinpi, cospi), (x->sincospi(x)[1], x->sincospi(x)[2]))
@test sinpi(x) Float64(sinpi(big(x)))
@test cospi(x) Float64(cospi(big(x)))
@test sinpi(complex(x, x)) Complex{Float64}(sinpi(complex(big(x), big(x))))
@test cospi(complex(x, x)) Complex{Float64}(cospi(complex(big(x), big(x))))
end
@test sinc(x) Float64(sinc(big(x)))
@test cosc(x) Float64(cosc(big(x)))
@test sinpi(complex(x, x)) Complex{Float64}(sinpi(complex(big(x), big(x))))
@test cospi(complex(x, x)) Complex{Float64}(cospi(complex(big(x), big(x))))
@test sinc(complex(x, x)) Complex{Float64}(sinc(complex(big(x), big(x))))
@test cosc(complex(x, x)) Complex{Float64}(cosc(complex(big(x), big(x))))
end
end

@testset "trig function type stability" begin
@testset "$T $f" for T = (Float32,Float64,BigFloat), f = (sind,cosd,sinpi,cospi)
@test Base.return_types(f,Tuple{T}) == [T]
@testset "$T $f" for T = (Float32,Float64,BigFloat,Rational{Int16},Complex{Int32},ComplexF16), f = (sind,cosd,sinpi,cospi)
@test Base.return_types(f,Tuple{T}) == [float(T)]
end
@testset "$T sincospi" for T = (Float32,Float64,BigFloat,Rational{Int16},Complex{Int32},ComplexF16)
@test Base.return_types(sincospi,Tuple{T}) == [Tuple{float(T),float(T)}]
end
end

Expand Down

2 comments on commit 6cd329c

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executing the daily benchmark build, I will reply here when finished:

@nanosoldier runbenchmarks(ALL, isdaily = true)

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @ararslan

Please sign in to comment.