Skip to content

Commit 2a4a61b

Browse files
committed
Systemize fixed point types to be tested
1 parent 196e410 commit 2a4a61b

File tree

4 files changed

+253
-207
lines changed

4 files changed

+253
-207
lines changed

test/common.jl

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using FixedPointNumbers, Statistics, Test
2+
using FixedPointNumbers: bitwidth, rawtype, nbitsfrac
3+
4+
function target_f(X::Type, T::Type{<:Integer}; ex = :default)
5+
f_min = target_f_min(X, T)
6+
f_max = bitwidth(T) - (T <: Signed) - 1 + f_min
7+
ex === :heavy && return f_min:f_max
8+
ex === :default && bitwidth(T) <= 16 && return f_min:f_max
9+
ex === :thin && return (f_max >> 1, f_max)
10+
if ex === :light || ex === :default
11+
itr = Iterators.filter(x -> x <= f_max, target_f_series(X, T))
12+
return (itr...,)
13+
end
14+
error()
15+
end
16+
17+
"""
18+
target(X::Type, Ss...; ex = :default)
19+
20+
Return a generator which enumerates the target types for testing.
21+
22+
# Arguments
23+
- `X`: target base type
24+
- `Ss`: symbols for specifying the target raw types
25+
- `:i*` : a `Signed` type if `X === Fixed`, or an `Unsigned` type if `X === Normed`
26+
- `:s*` : a `Signed` type (not yet supported)
27+
- `:u*` : an `Unsigned` type (not yet supported)
28+
- `ex`: exhaustivity of `f`s
29+
- `:heavy`: all supported `f`s
30+
- `:default` : same as `:heavy` for 8-/16-bit types, and same as `:light` otherwise
31+
- `:light` : important `f`s for byte boundaries and floating point types
32+
- `:thin` : maximum and half `f`s per type
33+
34+
# Example
35+
```julia
36+
julia> collect(target(Normed, :i8, :i32; ex = :default))
37+
21-element Array{DataType,1}:
38+
Normed{UInt8,1}
39+
Normed{UInt8,2}
40+
Normed{UInt8,3}
41+
Normed{UInt8,4}
42+
Normed{UInt8,5}
43+
Normed{UInt8,6}
44+
Normed{UInt8,7}
45+
Normed{UInt8,8}
46+
Normed{UInt32,1}
47+
Normed{UInt32,7}
48+
Normed{UInt32,8}
49+
Normed{UInt32,9}
50+
Normed{UInt32,10}
51+
Normed{UInt32,11}
52+
Normed{UInt32,15}
53+
Normed{UInt32,16}
54+
Normed{UInt32,17}
55+
Normed{UInt32,23}
56+
Normed{UInt32,24}
57+
Normed{UInt32,31}
58+
Normed{UInt32,32}
59+
```
60+
"""
61+
function target(X::Type, Ss...; ex = :default)
62+
Ts = symbol_to_inttype.(X, Ss)
63+
(X{T,f} for T in Ts for f in target_f(X, T; ex = ex))
64+
end
65+
target(X::Type; ex = :default) = target(X, :i8, :i16, :i32, :i64, :i128; ex = ex)

test/fixed.jl

Lines changed: 80 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
1-
using FixedPointNumbers, Statistics, Test
2-
using FixedPointNumbers: bitwidth
1+
include("common.jl")
2+
3+
target_f_min(::Type{Fixed}, T::Type{<:Integer}) = 0
4+
target_f_series(::Type{Fixed}, T::Type{<:Integer}) =
5+
(0, 1, 7, 8, 9,
6+
10, 11, 15, 16, 17,
7+
23, 24, 31, 32, 33,
8+
52, 53, 63, 64, 65,
9+
112, 113, 127)
10+
11+
function symbol_to_inttype(::Type{Fixed}, s::Symbol)
12+
d = Dict(:i8 => Int8, :i16 => Int16, :i32 => Int32, :i64 => Int64, :i128 => Int128)
13+
d[s]
14+
end
315

416
function test_op(fun::F, ::Type{T}, fx, fy, fxf, fyf, tol) where {F,T}
517
# Make sure that the result is representable
@@ -8,9 +20,9 @@ function test_op(fun::F, ::Type{T}, fx, fy, fxf, fyf, tol) where {F,T}
820
@assert abs(convert(Float64, fun(fx, fy)) - fun(fxf, fyf)) <= tol
921
end
1022

11-
function test_fixed(::Type{T}, f) where {T}
23+
function test_fixed(::Type{T}) where {T}
1224
values = [-10:0.01:10; -180:.01:-160; 160:.01:180]
13-
tol = 2.0^-f
25+
tol = Float64(eps(T))
1426
for x in values
1527
# Ignore values outside the representable range
1628
# typemin <, otherwise for -(-0.5) > typemax
@@ -36,7 +48,7 @@ function test_fixed(::Type{T}, f) where {T}
3648
fyf = convert(Float64, fy)
3749

3850
@assert fx==fy || x!=y
39-
@assert fx<fy || x>=y
51+
@assert fx<fy || (x + tol)>=y
4052
@assert fx<=fy || x>y
4153

4254
test_op(+, T, fx, fy, fxf, fyf, tol)
@@ -50,10 +62,8 @@ function test_fixed(::Type{T}, f) where {T}
5062
end
5163

5264
@testset "test_fixed" begin
53-
for (TI, f) in [(Int8, 7), (Int16, 8), (Int16, 10), (Int32, 16)]
54-
T = Fixed{TI,f}
55-
# println(" Testing $T")
56-
test_fixed(T, f)
65+
for F in target(Fixed, :i8, :i16, :i32; ex = :thin)
66+
test_fixed(F)
5767
end
5868
end
5969

@@ -93,10 +103,18 @@ end
93103
end
94104

95105
@testset "limits and identities" begin
96-
# TODO: add tests
97-
98-
# issue #79
99-
@test floatmin(Q11f4) == Q11f4(0.06)
106+
@testset "$F" for F in target(Fixed)
107+
T, f = rawtype(F), nbitsfrac(F)
108+
@test zero(F) == 0
109+
f < bitwidth(T) - 1 && @test one(F) == 1
110+
f < bitwidth(T) - 1 && @test one(F) * oneunit(F) == oneunit(F)
111+
@test typemin(F) == typemin(T) >> f
112+
@test typemax(F) == typemax(T)//big"2"^f
113+
@test floatmin(F) === eps(F) == 2.0^-f # issue #79
114+
@test floatmax(F) === typemax(F)
115+
@test eps(zero(F)) === eps(typemax(F))
116+
@test sizeof(F) == sizeof(T)
117+
end
100118
end
101119

102120
@testset "inexactness" begin
@@ -182,11 +200,15 @@ end
182200
@test big(0.75Q3f4)::BigFloat == big"0.75"
183201
end
184202

185-
@testset "float()" begin
203+
@testset "float/floattype" begin
186204
@test float(0.75Q3f4) === 0.75f0
187205
@test float(0.75Q19f12) === 0.75
188206
@test float(0.75Q7f24) === 0.75
189207
@test float(0.75Q10f53)::BigFloat == big"0.75"
208+
209+
@testset "floattype($F)" for F in target(Fixed, :i8, :i16, :i32, :i64; ex = :heavy)
210+
@test typemax(F) <= maxintfloat(floattype(F))
211+
end
190212
end
191213

192214
@testset "conversions to float" begin
@@ -195,49 +217,40 @@ end
195217
end
196218

197219
for Tf in (Float16, Float32, Float64)
198-
@testset "$Tf(::Fixed{$T})" for T in (Int8, Int16)
199-
@testset "$Tf(::Fixed{$T,$f})" for f = 0:bitwidth(T)-1
200-
F = Fixed{T,f}
201-
float_err = 0.0
202-
for i = typemin(T):typemax(T)
203-
f_expected = Tf(i * BigFloat(2)^-f)
204-
f_actual = Tf(reinterpret(F, i))
205-
float_err += abs(f_actual - f_expected)
206-
end
207-
@test float_err == 0.0
220+
@testset "$Tf(::$F)" for F in target(Fixed, :i8, :i16)
221+
T, f = rawtype(F), nbitsfrac(F)
222+
float_err = 0.0
223+
for i = typemin(T):typemax(T)
224+
f_expected = Tf(i * BigFloat(2)^-f)
225+
f_actual = Tf(reinterpret(F, i))
226+
float_err += abs(f_actual - f_expected)
208227
end
228+
@test float_err == 0.0
209229
end
210-
@testset "$Tf(::Fixed{$T})" for T in (Int32, Int64, Int128)
211-
@testset "$Tf(::Fixed{$T,$f})" for f = 0:bitwidth(T)-1
212-
F = Fixed{T,f}
213-
error_count = 0
214-
for i in vcat(typemin(T):(typemin(T)+0xFF),
215-
-T(0xFF):T(0xFF),
216-
(typemax(T)-0xFF):typemax(T))
217-
f_expected = Tf(i * BigFloat(2)^-f)
218-
isinf(f_expected) && break # for Float16() and Float32()
219-
f_actual = Tf(reinterpret(F, i))
220-
f_actual == f_expected && continue
221-
error_count += 1
222-
end
223-
@test error_count == 0
230+
@testset "$Tf(::$F)" for F in target(Fixed, :i32, :i64, :i128)
231+
T, f = rawtype(F), nbitsfrac(F)
232+
error_count = 0
233+
for i in vcat(typemin(T):(typemin(T)+0xFF),
234+
-T(0xFF):T(0xFF),
235+
(typemax(T)-0xFF):typemax(T))
236+
f_expected = Tf(i * BigFloat(2)^-f)
237+
isinf(f_expected) && break # for Float16() and Float32()
238+
f_actual = Tf(reinterpret(F, i))
239+
f_actual == f_expected && continue
240+
error_count += 1
224241
end
242+
@test error_count == 0
225243
end
226244
end
227245
end
228246

229247
@testset "fractional fixed-point numbers" begin
230248
# test all-fractional fixed-point numbers (issue #104)
231-
for (T, f) in ((Int8, 7),
232-
(Int16, 15),
233-
(Int32, 31),
234-
(Int64, 63))
235-
tmax = typemax(Fixed{T, f})
236-
@test tmax == BigInt(typemax(T)) / BigInt(2)^f
237-
tol = (tmax + BigFloat(1.0)) / bitwidth(T)
238-
for x in range(-1, stop=BigFloat(tmax)-tol, length=50)
239-
@test abs(Fixed{T, f}(x) - x) <= tol
240-
end
249+
for F in (Q0f7, Q0f15, Q0f31, Q0f63)
250+
tmax = typemax(F)
251+
tol = (tmax + BigFloat(1.0)) / bitwidth(F)
252+
r = range(-1, stop=BigFloat(tmax)-tol, length=50)
253+
@test all(x -> abs(F(x) - x) <= tol, r)
241254
end
242255
end
243256

@@ -258,15 +271,15 @@ end
258271
end
259272

260273
@testset "rounding" begin
261-
for T in (Int8, Int16, Int32, Int64)
274+
for sym in (:i8, :i16, :i32, :i64)
275+
T = symbol_to_inttype(Fixed, sym)
262276
rs = vcat([ oneunit(T) << b - oneunit(T) for b = 0:bitwidth(T)-1],
263277
[ oneunit(T) << b for b = 1:bitwidth(T)-2],
264278
[ oneunit(T) << b + oneunit(T) for b = 2:bitwidth(T)-2],
265279
[-oneunit(T) << b - oneunit(T) for b = 2:bitwidth(T)-2],
266280
[-oneunit(T) << b for b = 1:bitwidth(T)-1],
267281
[-oneunit(T) << b + oneunit(T) for b = 1:bitwidth(T)-1])
268-
@testset "rounding Fixed{$T,$f}" for f = 0:bitwidth(T)-1
269-
F = Fixed{T,f}
282+
@testset "rounding $F" for F in target(Fixed, sym)
270283
xs = (reinterpret(F, r) for r in rs)
271284
@test all(x -> trunc(x) == trunc(float(x)), xs)
272285
@test all(x -> floor(float(x)) < typemin(F) || floor(x) == floor(float(x)), xs)
@@ -308,11 +321,11 @@ end
308321
end
309322

310323
@testset "approx" begin
311-
@testset "approx $T" for T in [Fixed{Int8,7}, Fixed{Int16,8}, Fixed{Int16,10}]
312-
xs = typemin(T):eps(T):typemax(T)-eps(T)
313-
@test all(x -> x x + eps(T), xs)
314-
@test all(x -> x + eps(T) x, xs)
315-
@test !any(x -> x - eps(T) x + eps(T), xs)
324+
@testset "approx $F" for F in target(Fixed, :i8, :i16; ex = :light)
325+
xs = typemin(F):eps(F):typemax(F)-eps(F)
326+
@test all(x -> x x + eps(F), xs)
327+
@test all(x -> x + eps(F) x, xs)
328+
@test !any(x -> x - eps(F) x + eps(F), xs)
316329
end
317330
end
318331

@@ -357,22 +370,16 @@ end
357370
@test !isinf(1Q7f8)
358371

359372
@testset "isinteger" begin
360-
for T in (Int8, Int16)
361-
@testset "isinteger(::Fixed{$T,$f})" for f = 0:bitwidth(T)-1
362-
F = Fixed{T,f}
363-
xs = typemin(F):eps(F):typemax(F)
364-
@test all(x -> isinteger(x) == isinteger(float(x)), xs)
365-
end
373+
@testset "isinteger(::$F)" for F in target(Fixed, :i8, :i16)
374+
xs = typemin(F):eps(F):typemax(F)
375+
@test all(x -> isinteger(x) == isinteger(float(x)), xs)
366376
end
367-
for T in (Int32, Int64)
368-
@testset "isinteger(::Fixed{$T,$f})" for f = 0:bitwidth(T)-1
369-
F = Fixed{T,f}
370-
fzero, fmax, fmin = zero(F), typemax(F), typemin(F)
371-
if f == 0
372-
@test isinteger(fzero) & isinteger(fmax) & isinteger(fmin)
373-
else
374-
@test isinteger(fzero) & !isinteger(fmax) & isinteger(fmin)
375-
end
377+
@testset "isinteger(::$F)" for F in target(Fixed, :i32, :i64, :i128)
378+
fzero, fmax, fmin = zero(F), typemax(F), typemin(F)
379+
if nbitsfrac(F) == 0
380+
@test isinteger(fzero) & isinteger(fmax) & isinteger(fmin)
381+
else
382+
@test isinteger(fzero) & !isinteger(fmax) & isinteger(fmin)
376383
end
377384
end
378385
@testset "isinteger(::Fixed{Int8,8})" begin # TODO: remove this testset
@@ -436,10 +443,10 @@ end
436443
end
437444

438445
@testset "rand" begin
439-
for F in (Fixed{Int8,7}, Fixed{Int16,8}, Fixed{Int16,10}, Fixed{Int32,16})
446+
@testset "rand(::$F)" for F in target(Fixed; ex = :thin)
440447
@test isa(rand(F), F)
441448
a = rand(F, (3, 5))
442-
@test ndims(a) == 2 && eltype(a) == F
449+
@test ndims(a) == 2 && eltype(a) === F
443450
@test size(a) == (3,5)
444451
end
445452
end

0 commit comments

Comments
 (0)