diff --git a/base/special/trig.jl b/base/special/trig.jl index 96a2da5f5b968..2137f1486204d 100644 --- a/base/special/trig.jl +++ b/base/special/trig.jl @@ -1112,6 +1112,7 @@ function _cosc(x::Number) # generic Taylor series: π ∑ (-1)^n (πx)^{2n-1}/a(n) where # a(n) = (1+2n)*(2n-1)! (= OEIS A174549) s = (term = -(π*x))/3 + iszero(s) && return s # preserve floating-point signed zero π²x² = term^2 ε = eps(fastabs(term)) # error threshold to stop sum n = 1 diff --git a/test/math.jl b/test/math.jl index dea1c8035d5eb..529f4e01405a9 100644 --- a/test/math.jl +++ b/test/math.jl @@ -590,6 +590,39 @@ end @test ismissing(scdm[2]) end +@testset "behavior at signed zero of monotonic floating-point functions mapping zero to zero" begin + function rounder(rm::RoundingMode) + function closure(::Type{T}, x::AbstractFloat) where {T <: AbstractFloat} + round(T, x, rm) + end + end + rounding_modes = ( + RoundNearest, RoundNearestTiesAway, RoundNearestTiesUp, RoundToZero, RoundFromZero, RoundUp, RoundDown, + ) + rounders = map(rounder, rounding_modes) + @testset "typ: $typ" for typ in (Float16, Float32, Float64, BigFloat) + (n0, n1) = typ.(0:1) + rounders_typ = Base.Fix1.(rounders, typ) + @testset "f: $f" for f in ( + # all strictly increasing + identity, deg2rad, rad2deg, cbrt, log1p, expm1, sinh, tanh, asinh, atanh, + sin, sind, sinpi, tan, tand, tanpi, asin, asind, atan, atand, Base.Fix2(atan, n1), Base.Fix2(atand, n1), + Base.Fix1(round, typ), Base.Fix1(trunc, typ), +, ∘(-, -), ∘(-, cosc), + rounders_typ..., + Base.Fix1(*, n1), Base.Fix2(*, n1), Base.Fix2(/, n1), + ) + @testset "s: $s" for s in (-1, 1) + z = s * n0 + z::typ + @test z == f(z)::typ + @test signbit(z) === signbit(f(z)) + isbitstype(typ) && + @test z === @inferred f(z) + end + end + end +end + @testset "Integer and Inf args for sinpi/cospi/tanpi/sinc/cosc" begin for (sinpi, cospi) in ((sinpi, cospi), (x->sincospi(x)[1], x->sincospi(x)[2])) @test sinpi(1) === 0.0