From e6797cc496314af3b5645dbd362e921d0c4365e9 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Wed, 13 Aug 2025 17:13:47 +0200 Subject: [PATCH 01/10] test: math: test floating-point behavior when mapping zero to zero The idea for this addition to the test suite originates here, where stevengj suggested to do this test for `cosc` specifically: * https://github.com/JuliaLang/julia/pull/59087#issuecomment-3183764523 This PR instead tests all applicable functions I could find. --- test/math.jl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/math.jl b/test/math.jl index dea1c8035d5eb..c150d71a75cc6 100644 --- a/test/math.jl +++ b/test/math.jl @@ -590,6 +590,23 @@ end @test ismissing(scdm[2]) end +@testset "behavior at signed zero of monotonic floating-point functions mapping zero to zero" begin + @testset "typ: $typ" for typ in (Float32, Float64) + @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.Fix1(round, typ), Base.Fix1(trunc, typ), ∘(-, -), ∘(-, cosc), + ) + @testset "s: $s" for s in (-1, 1) + z = s * typ(0) + z::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 From 1c7c31aff907e4e455fa9ffca6bbefd59ade766e Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Wed, 13 Aug 2025 17:19:22 +0200 Subject: [PATCH 02/10] add `Float16` --- test/math.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/math.jl b/test/math.jl index c150d71a75cc6..5e785d2e59882 100644 --- a/test/math.jl +++ b/test/math.jl @@ -591,7 +591,7 @@ end end @testset "behavior at signed zero of monotonic floating-point functions mapping zero to zero" begin - @testset "typ: $typ" for typ in (Float32, Float64) + @testset "typ: $typ" for typ in (Float16, Float32, Float64) @testset "f: $f" for f in ( # all strictly increasing identity, deg2rad, rad2deg, cbrt, log1p, expm1, sinh, tanh, asinh, atanh, From 5337a99ee6f0eeb4f2f32b09f0ddcf284b33fa6e Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:37:47 +0200 Subject: [PATCH 03/10] unary addition Co-authored-by: Steven G. Johnson --- test/math.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/math.jl b/test/math.jl index 5e785d2e59882..9b590cdc0d6d1 100644 --- a/test/math.jl +++ b/test/math.jl @@ -596,7 +596,7 @@ end # all strictly increasing identity, deg2rad, rad2deg, cbrt, log1p, expm1, sinh, tanh, asinh, atanh, sin, sind, sinpi, tan, tand, tanpi, asin, asind, atan, atand, - Base.Fix1(round, typ), Base.Fix1(trunc, typ), ∘(-, -), ∘(-, cosc), + Base.Fix1(round, typ), Base.Fix1(trunc, typ), (+), ∘(-, -), ∘(-, cosc), ) @testset "s: $s" for s in (-1, 1) z = s * typ(0) From 946e00505ea3adeb0fb7743a8163715eac4e71ce Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Thu, 14 Aug 2025 10:38:14 +0200 Subject: [PATCH 04/10] binary `atan` Co-authored-by: Steven G. Johnson --- test/math.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/math.jl b/test/math.jl index 9b590cdc0d6d1..ab33046265b08 100644 --- a/test/math.jl +++ b/test/math.jl @@ -595,7 +595,7 @@ end @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, + sin, sind, sinpi, tan, tand, tanpi, asin, asind, atan, atand, Base.Fix2(atan, one(typ)), Base.Fix2(atand, one(typ)), Base.Fix1(round, typ), Base.Fix1(trunc, typ), (+), ∘(-, -), ∘(-, cosc), ) @testset "s: $s" for s in (-1, 1) From 1b55014a7462860cd13ac54cd59470e5ad37964c Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 14 Aug 2025 10:39:28 +0200 Subject: [PATCH 05/10] delete redundant parentheses --- test/math.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/math.jl b/test/math.jl index ab33046265b08..7671adb3cf0d7 100644 --- a/test/math.jl +++ b/test/math.jl @@ -596,7 +596,7 @@ end # 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, one(typ)), Base.Fix2(atand, one(typ)), - Base.Fix1(round, typ), Base.Fix1(trunc, typ), (+), ∘(-, -), ∘(-, cosc), + Base.Fix1(round, typ), Base.Fix1(trunc, typ), +, ∘(-, -), ∘(-, cosc), ) @testset "s: $s" for s in (-1, 1) z = s * typ(0) From d5bf802c53efa7ec227e5f64e864745526188e01 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 14 Aug 2025 10:42:26 +0200 Subject: [PATCH 06/10] deduplicate, hoist computation out of loop --- test/math.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/math.jl b/test/math.jl index 7671adb3cf0d7..4175dd6ef2402 100644 --- a/test/math.jl +++ b/test/math.jl @@ -592,14 +592,16 @@ end @testset "behavior at signed zero of monotonic floating-point functions mapping zero to zero" begin @testset "typ: $typ" for typ in (Float16, Float32, Float64) + n0 = typ(0) + n1 = typ(1) @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, one(typ)), Base.Fix2(atand, one(typ)), + 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), ) @testset "s: $s" for s in (-1, 1) - z = s * typ(0) + z = s * n0 z::typ @test z === @inferred f(z) end From 7efdf56b6e00ffe3b94dcd7091f77d33d6d9dad5 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 14 Aug 2025 10:46:14 +0200 Subject: [PATCH 07/10] add some identity functions as suggested by stevengj --- test/math.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/math.jl b/test/math.jl index 4175dd6ef2402..0ea1a1d03b986 100644 --- a/test/math.jl +++ b/test/math.jl @@ -599,6 +599,7 @@ end 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), + Base.Fix1(*, n1), Base.Fix2(*, n1), Base.Fix2(/, n1), ) @testset "s: $s" for s in (-1, 1) z = s * n0 From 11648ba5cdfda68bc906f53c9547769171809126 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 14 Aug 2025 10:52:44 +0200 Subject: [PATCH 08/10] less lines of code --- test/math.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/math.jl b/test/math.jl index 0ea1a1d03b986..10697f3009cc0 100644 --- a/test/math.jl +++ b/test/math.jl @@ -592,8 +592,7 @@ end @testset "behavior at signed zero of monotonic floating-point functions mapping zero to zero" begin @testset "typ: $typ" for typ in (Float16, Float32, Float64) - n0 = typ(0) - n1 = typ(1) + (n0, n1) = typ.(0:1) @testset "f: $f" for f in ( # all strictly increasing identity, deg2rad, rad2deg, cbrt, log1p, expm1, sinh, tanh, asinh, atanh, From 5425a090da8091e4a590ab7891b79fb672d276c2 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 14 Aug 2025 11:04:15 +0200 Subject: [PATCH 09/10] also test other rounding modes --- test/math.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/math.jl b/test/math.jl index 10697f3009cc0..6de43c5f7b499 100644 --- a/test/math.jl +++ b/test/math.jl @@ -591,13 +591,24 @@ end 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) (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) From d88bb93c0290ee22d25663885808cc0586aa8b64 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Thu, 14 Aug 2025 11:11:28 +0200 Subject: [PATCH 10/10] also test `BigFloat`, with `cosc` fix as suggested by stevengj --- base/special/trig.jl | 1 + test/math.jl | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) 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 6de43c5f7b499..529f4e01405a9 100644 --- a/test/math.jl +++ b/test/math.jl @@ -600,7 +600,7 @@ end RoundNearest, RoundNearestTiesAway, RoundNearestTiesUp, RoundToZero, RoundFromZero, RoundUp, RoundDown, ) rounders = map(rounder, rounding_modes) - @testset "typ: $typ" for typ in (Float16, Float32, Float64) + @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 ( @@ -614,6 +614,9 @@ end @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