Skip to content

Commit 2ebf6a2

Browse files
committed
test accuracy
1 parent becc964 commit 2ebf6a2

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

test/math.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
include("testhelpers/EvenIntegers.jl")
44
using .EvenIntegers
55

6+
include("testhelpers/ULPError.jl")
7+
using .ULPError
8+
69
using Random
710
using LinearAlgebra
811
using Base.Experimental: @force_compile
@@ -652,6 +655,14 @@ end
652655
@test cosc(big"0.5") big"-1.273239544735162686151070106980114896275677165923651589981338752471174381073817" rtol=1e-76
653656
@test cosc(big"0.499") big"-1.272045747741181369948389133250213864178198918667041860771078493955590574971317" rtol=1e-76
654657
end
658+
659+
@testset "accuracy of `cosc` around the origin" begin
660+
for t in (Float32, Float64)
661+
for x in range(start = t(-1), stop = t(1), length = 5000)
662+
@test ulp_error(cosc, x) < 4
663+
end
664+
end
665+
end
655666
end
656667

657668
@testset "Irrational args to sinpi/cospi/tanpi/sinc/cosc" begin

test/testhelpers/ULPError.jl

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
module ULPError
2+
export ulp_error, ulp_error_maximum
3+
@noinline function throw_invalid()
4+
throw(ArgumentError("invalid"))
5+
end
6+
function ulp_error(accurate::AbstractFloat, approximate::AbstractFloat)
7+
if isnan(accurate)
8+
@noinline throw_invalid()
9+
end
10+
if isnan(approximate)
11+
@noinline throw_invalid()
12+
end
13+
# the ULP error is usually not required to great accuracy, so `Float32` should be precise enough
14+
zero_return = Float32(0)
15+
inf_return = Float32(Inf)
16+
if isinf(accurate) || iszero(accurate) # handle floating-point edge cases
17+
if isinf(accurate)
18+
if isinf(approximate) && (signbit(accurate) == signbit(approximate))
19+
return zero_return
20+
end
21+
return inf_return
22+
end
23+
# `iszero(accurate)`
24+
if iszero(approximate)
25+
return zero_return
26+
end
27+
return inf_return
28+
end
29+
# assuming `precision(BigFloat)` is great enough
30+
acc = if accurate isa BigFloat
31+
accurate
32+
else
33+
BigFloat(accurate)::BigFloat
34+
end
35+
err = abs(Float32((approximate - acc) / eps(approximate))::Float32)
36+
if isnan(err)
37+
@noinline throw_invalid() # unexpected
38+
end
39+
err
40+
end
41+
function ulp_error(accurate::Acc, approximate::App, x::AbstractFloat) where {Acc, App}
42+
acc = accurate(x)
43+
app = approximate(x)
44+
ulp_error(acc, app)
45+
end
46+
function ulp_error(func::Func, x::AbstractFloat) where {Func}
47+
ulp_error(func BigFloat, func, x)
48+
end
49+
function ulp_error_maximum(func::Func, iterator) where {Func}
50+
function f(x::AbstractFloat)
51+
ulp_error(func, x)
52+
end
53+
maximum(f, iterator)
54+
end
55+
end

0 commit comments

Comments
 (0)