Skip to content

Commit 03057d4

Browse files
authored
Modify Fixed constructors (#170)
This adds explicit input range checks. As a result, the constructors throw `ArgumentError` instead of `InexactError` for out-of-range inputs.
1 parent 62cef1c commit 03057d4

File tree

2 files changed

+40
-10
lines changed

2 files changed

+40
-10
lines changed

src/fixed.jl

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,39 @@ fracmask(x::Fixed{T,f}) where {T, f} = ~intmask(x) # Signed
4848

4949
# constructor-style conversions
5050
function _convert(::Type{F}, x::Fixed{T2,f2}) where {T, T2, f, f2, F <: Fixed{T,f}}
51-
y = round(((1<<f)/(1<<f2))*reinterpret(x)) # FIXME: avoid overflow
51+
y = round(@exp2(f-f2) * reinterpret(x))
5252
(typemin(T) <= y) & (y <= typemax(T)) || throw_converterror(F, x)
5353
reinterpret(F, _unsafe_trunc(T, y))
5454
end
5555

5656
function _convert(::Type{F}, x::Integer) where {T, f, F <: Fixed{T,f}}
57-
reinterpret(F, round(T, convert(widen1(T),x)<<f)) # TODO: optimization and input range checking
57+
if ((typemin(T) >> f) <= x) & (x <= (typemax(T) >> f))
58+
reinterpret(F, unsafe_trunc(T, x) << f)
59+
else
60+
throw_converterror(F, x)
61+
end
5862
end
5963

6064
function _convert(::Type{F}, x::AbstractFloat) where {T, f, F <: Fixed{T,f}}
61-
reinterpret(F, round(T, trunc(widen1(T),x)<<f + rem(x,1)*(one(widen1(T))<<f))) # TODO: optimization and input range checking
65+
bigx = big(x)
66+
bmin = BigFloat(typemin(F)) - @exp2(-f-1)
67+
bmax = BigFloat(typemax(F)) + @exp2(-f-1)
68+
if bmin <= bigx < bmax
69+
reinterpret(F, round(T, bigx * @exp2(f)))
70+
else
71+
throw_converterror(F, x)
72+
end
73+
end
74+
75+
_convert(::Type{F}, x::Float16) where {T, f, F <: Fixed{T,f}} = F(Float32(x))
76+
77+
function _convert(::Type{F}, x::Union{Float32, Float64}) where {T, f, F <: Fixed{T,f}}
78+
Tf = typeof(x)
79+
if Tf(typemin(F) - @exp2(-f-1)) <= x < Tf(typemax(F) + @exp2(-f-1))
80+
reinterpret(F, round(T, x * @exp2(f)))
81+
else
82+
throw_converterror(F, x)
83+
end
6284
end
6385

6486
function _convert(::Type{F}, x::Rational) where {T, f, F <: Fixed{T,f}}

test/fixed.jl

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ end
8282
end
8383

8484
@testset "inexactness" begin
85-
@test_throws InexactError Q0f7(-2)
8685
# TODO: change back to InexactError when it allows message strings
86+
@test_throws ArgumentError Q0f7(-2)
8787
@test_throws ArgumentError one(Q0f15)
8888
@test_throws ArgumentError oneunit(Q0f31)
8989
@test_throws ArgumentError one(Fixed{Int8,8}) # TODO: remove this at end of its support
@@ -92,11 +92,19 @@ end
9292
@testset "conversion" begin
9393
@test isapprox(convert(Fixed{Int8,7}, 0.8), 0.797, atol=0.001)
9494
@test isapprox(convert(Fixed{Int8,7}, 0.9), 0.898, atol=0.001)
95-
@test_throws InexactError convert(Fixed{Int8, 7}, 0.999)
96-
@test_throws InexactError convert(Fixed{Int8, 7}, 1.0)
97-
@test_throws InexactError convert(Fixed{Int8, 7}, 1)
98-
@test_throws InexactError convert(Fixed{Int8, 7}, 2)
99-
@test_throws InexactError convert(Fixed{Int8, 7}, 128)
95+
@test_throws ArgumentError convert(Fixed{Int8, 7}, 0.999)
96+
@test_throws ArgumentError convert(Fixed{Int8, 7}, 1.0)
97+
@test_throws ArgumentError convert(Fixed{Int8, 7}, 1)
98+
@test_throws ArgumentError convert(Fixed{Int8, 7}, 2)
99+
@test_throws ArgumentError convert(Fixed{Int8, 7}, 128)
100+
@test_throws ArgumentError convert(Fixed{Int8, 7}, 1.0)
101+
102+
@test convert(Q0f7, -128.5/128) == -1
103+
104+
@test convert(Q0f7, -0.75f0) == -0.75
105+
@test convert(Q0f7, Float16(-0.75)) == -0.75
106+
@test convert(Q0f7, BigFloat(-0.75)) == -0.75
107+
@test_throws ArgumentError convert(Q0f7, BigFloat(127.5/128))
100108

101109
@test convert(Q2f5, -1//2) === -0.5Q2f5
102110
@test convert(Q1f6, Rational{Int8}(-3//4)) === -0.75Q1f6
@@ -105,7 +113,7 @@ end
105113
@test_throws ArgumentError convert(Q0f7, typemax(Rational{Int8}))
106114

107115
@test convert(Q0f7, Base.TwicePrecision(0.5)) === 0.5Q0f7
108-
@test_throws InexactError convert(Q7f8, Base.TwicePrecision(0x80, 0x01))
116+
@test_throws ArgumentError convert(Q7f8, Base.TwicePrecision(0x80, 0x01))
109117
tp = Base.TwicePrecision(0xFFFFFFFFp-32, 0xFFFFFFFEp-64)
110118
@test convert(Q0f63, tp) === reinterpret(Q0f63, typemax(Int64))
111119
end

0 commit comments

Comments
 (0)