Skip to content

Commit 020c74f

Browse files
authored
Merge pull request #53 from JuliaMath/teh/convert_promote
Implement promotion among UFixed, make convert errors more informative
2 parents fe244dd + 526a7b9 commit 020c74f

File tree

3 files changed

+84
-25
lines changed

3 files changed

+84
-25
lines changed

src/FixedPointNumbers.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,13 @@ end
109109
const _log2_10 = 3.321928094887362
110110
showcompact{T,f}(io::IO, x::FixedPoint{T,f}) = show(io, round(convert(Float64,x), ceil(Int,f/_log2_10)))
111111

112+
@noinline function throw_converterror{T<:FixedPoint}(::Type{T}, x)
113+
n = 2^(8*sizeof(T))
114+
bitstring = sizeof(T) == 1 ? "an 8-bit" : "a $(8*sizeof(T))-bit"
115+
io = IOBuffer()
116+
showcompact(io, typemin(T)); Tmin = takebuf_string(io)
117+
showcompact(io, typemax(T)); Tmax = takebuf_string(io)
118+
throw(ArgumentError("$T is $bitstring type representing $n values from $Tmin to $Tmax; cannot represent $x"))
119+
end
120+
112121
end # module

src/ufixed.jl

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ immutable UFixed{T<:Unsigned,f} <: FixedPoint{T,f}
99
end
1010

1111
rawtype{T,f}(::Type{UFixed{T,f}}) = T
12+
rawtype(x::Number) = rawtype(typeof(x))
1213
nbitsfrac{T,f}(::Type{UFixed{T,f}}) = f
14+
nbitsfrac(x::Number) = nbitsfract(typeof(x))
1315

1416
typealias UFixed8 UFixed{UInt8,8}
1517
typealias UFixed10 UFixed{UInt16,10}
@@ -31,29 +33,40 @@ const uf14 = UFixedConstructor{UInt16,14}()
3133
const uf16 = UFixedConstructor{UInt16,16}()
3234

3335
zero{T,f}(::Type{UFixed{T,f}}) = UFixed{T,f}(zero(T),0)
34-
@generated function one{T<:UFixed}(::Type{T})
35-
f = 2^nbitsfrac(T)-1
36-
:( T($f,0) )
36+
function one{T<:UFixed}(::Type{T})
37+
T(typemax(rawtype(T)) >> (8*sizeof(T)-nbitsfrac(T)), 0)
3738
end
3839
zero(x::UFixed) = zero(typeof(x))
3940
one(x::UFixed) = one(typeof(x))
4041
rawone(v) = reinterpret(one(v))
4142

4243
# Conversions
4344
convert{T<:UFixed}(::Type{T}, x::T) = x
44-
convert{T1<:UFixed}(::Type{T1}, x::UFixed) = reinterpret(T1, round(rawtype(T1), (rawone(T1)/rawone(x))*reinterpret(x)))
45+
function convert{T<:UFixed}(::Type{T}, x::UFixed)
46+
y = round((rawone(T)/rawone(x))*reinterpret(x))
47+
(0 <= y) & (y <= typemax(rawtype(T))) || throw_converterror(T, x)
48+
reinterpret(T, _unsafe_trunc(rawtype(T), y))
49+
end
4550
convert(::Type{UFixed16}, x::UFixed8) = reinterpret(UFixed16, convert(UInt16, 0x0101*reinterpret(x)))
4651
convert{U<:UFixed}(::Type{U}, x::Real) = _convert(U, rawtype(U), x)
47-
_convert{U<:UFixed,T}(::Type{U}, ::Type{T}, x) = U(round(T, widen1(rawone(U))*x), 0)
48-
_convert{U<:UFixed }(::Type{U}, ::Type{UInt128}, x) = U(round(UInt128, rawone(U)*x), 0)
52+
function _convert{U<:UFixed,T}(::Type{U}, ::Type{T}, x)
53+
y = round(widen1(rawone(U))*x)
54+
(0 <= y) & (y <= typemax(T)) || throw_converterror(U, x)
55+
U(_unsafe_trunc(T, y), 0)
56+
end
57+
function _convert{U<:UFixed}(::Type{U}, ::Type{UInt128}, x)
58+
y = round(rawone(U)*x) # for UInt128, we can't widen
59+
(0 <= y) & (y <= typemax(UInt128)) & (x <= Float64(typemax(U))) || throw_converterror(U, x)
60+
U(_unsafe_trunc(UInt128, y), 0)
61+
end
4962

5063
rem{T<:UFixed}(x::T, ::Type{T}) = x
51-
rem{T<:UFixed}(x::UFixed, ::Type{T}) = reinterpret(T, unsafe_trunc(rawtype(T), round((rawone(T)/rawone(x))*reinterpret(x))))
52-
rem{T<:UFixed}(x::Real, ::Type{T}) = reinterpret(T, unsafe_trunc(rawtype(T), round(rawone(T)*x)))
64+
rem{T<:UFixed}(x::UFixed, ::Type{T}) = reinterpret(T, _unsafe_trunc(rawtype(T), round((rawone(T)/rawone(x))*reinterpret(x))))
65+
rem{T<:UFixed}(x::Real, ::Type{T}) = reinterpret(T, _unsafe_trunc(rawtype(T), round(rawone(T)*x)))
5366

5467
convert(::Type{BigFloat}, x::UFixed) = reinterpret(x)*(1/BigFloat(rawone(x)))
5568
function convert{T<:AbstractFloat}(::Type{T}, x::UFixed)
56-
y = reinterpret(x)*(1/convert(T, rawone(x)))
69+
y = reinterpret(x)*(one(rawtype(x))/convert(T, rawone(x)))
5770
convert(T, y) # needed for types like Float16 which promote arithmetic to Float32
5871
end
5972
convert(::Type{Bool}, x::UFixed) = x == zero(x) ? false : true
@@ -68,17 +81,21 @@ sizeof{T<:UFixed}(::Type{T}) = sizeof(rawtype(T))
6881
abs(x::UFixed) = x
6982

7083
# Arithmetic
84+
@generated function floattype{U<:UFixed}(::Type{U})
85+
eps(U) < eps(Float32) ? :(Float64) : :(Float32)
86+
end
87+
7188
(-){T<:UFixed}(x::T) = T(-reinterpret(x), 0)
7289
(~){T<:UFixed}(x::T) = T(~reinterpret(x), 0)
7390

7491
+{T,f}(x::UFixed{T,f}, y::UFixed{T,f}) = UFixed{T,f}(convert(T, x.i+y.i),0)
7592
-{T,f}(x::UFixed{T,f}, y::UFixed{T,f}) = UFixed{T,f}(convert(T, x.i-y.i),0)
76-
*{T<:UFixed}(x::T, y::T) = convert(T,convert(Float32, x)*convert(Float32, y))
77-
/{T<:UFixed}(x::T, y::T) = convert(T,convert(Float32, x)/convert(Float32, y))
93+
*{T<:UFixed}(x::T, y::T) = convert(T,convert(floattype(T), x)*convert(floattype(T), y))
94+
/{T<:UFixed}(x::T, y::T) = convert(T,convert(floattype(T), x)/convert(floattype(T), y))
7895

7996
# Comparisons
8097
<{T<:UFixed}(x::T, y::T) = reinterpret(x) < reinterpret(y)
81-
<={T<:UFixed}(x::T, y::T) = reinterpret(x) < reinterpret(y)
98+
<={T<:UFixed}(x::T, y::T) = reinterpret(x) <= reinterpret(y)
8299

83100
# Functions
84101
trunc{T<:UFixed}(x::T) = T(div(reinterpret(x), rawone(T))*rawone(T),0)
@@ -150,3 +167,18 @@ promote_rule{T<:UFixed, R<:Rational}(::Type{T}, ::Type{R}) = R
150167
Tp = eps(convert(Float32, typemax(Ti))) > eps(T) ? Float64 : Float32
151168
:( $Tp )
152169
end
170+
@generated function promote_rule{T1,T2,f1,f2}(::Type{UFixed{T1,f1}}, ::Type{UFixed{T2,f2}})
171+
f = max(f1, f2) # ensure we have enough precision
172+
T = promote_type(T1, T2)
173+
# make sure we have enough integer bits
174+
i1, i2 = 8*sizeof(T1)-f1, 8*sizeof(T2)-f2 # number of integer bits for each
175+
i = 8*sizeof(T)-f
176+
while i < max(i1, i2)
177+
T = widen1(T)
178+
i = 8*sizeof(T)-f
179+
end
180+
:(UFixed{$T,$f})
181+
end
182+
183+
_unsafe_trunc{T}(::Type{T}, x::Integer) = x % T
184+
_unsafe_trunc{T}(::Type{T}, x) = unsafe_trunc(T, x)

test/ufixed.jl

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ v = @compat UFixed12.([2])
2121
@test v == UFixed12[0x1ffeuf12]
2222
@test isa(v, Vector{UFixed12})
2323

24-
UF2 = (UFixed{UInt32,16}, UFixed{UInt64,3}, UFixed{UInt128,7})
24+
UF2 = (UFixed{UInt32,16}, UFixed{UInt64,3}, UFixed{UInt64,51}, UFixed{UInt128,7}, UFixed{UInt128,51})
2525

2626
for T in (FixedPointNumbers.UF..., UF2...)
2727
@test zero(T) == 0
@@ -43,15 +43,19 @@ end
4343
@test typemax(UFixed{UInt32,16}) == typemax(UInt32) // (2^16-1)
4444
@test typemax(UFixed{UInt64,3}) == typemax(UInt64) // (2^3-1)
4545
@test typemax(UFixed{UInt128,7}) == typemax(UInt128) // (2^7-1)
46-
47-
@test_throws InexactError UFixed8(2)
48-
@test_throws InexactError UFixed8(255)
49-
@test_throws InexactError UFixed8(0xff)
50-
@test_throws InexactError UFixed16(2)
51-
@test_throws InexactError UFixed16(0xff)
52-
@test_throws InexactError UFixed16(0xffff)
53-
@test_throws InexactError convert(UFixed8, typemax(UFixed10))
54-
@test_throws InexactError convert(UFixed16, typemax(UFixed10))
46+
@test typemax(UFixed{UInt128,100}) == typemax(UInt128) // (UInt128(2)^100-1)
47+
48+
# TODO: change back to InexactError when it allows message strings
49+
@test_throws ArgumentError UFixed8(2)
50+
@test_throws ArgumentError UFixed8(255)
51+
@test_throws ArgumentError UFixed8(0xff)
52+
@test_throws ArgumentError UFixed16(2)
53+
@test_throws ArgumentError UFixed16(0xff)
54+
@test_throws ArgumentError UFixed16(0xffff)
55+
@test_throws ArgumentError convert(UFixed8, typemax(UFixed10))
56+
@test_throws ArgumentError convert(UFixed16, typemax(UFixed10))
57+
@test_throws ArgumentError convert(UFixed{UInt128,100}, 10^9)
58+
@test_throws ArgumentError convert(UFixed{UInt128,100}, 10.0^9)
5559

5660
x = UFixed8(0.5)
5761
@test isfinite(x) == true
@@ -64,7 +68,7 @@ x = UFixed8(0.5)
6468
@test convert(UFixed14, 1.1/typemax(UInt16)*4) == eps(UFixed14)
6569
@test convert(UFixed16, 1.1/typemax(UInt16)) == eps(UFixed16)
6670
@test convert(UFixed{UInt32,16}, 1.1/typemax(UInt32)*2^16) == eps(UFixed{UInt32,16})
67-
@test convert(UFixed{UInt64,3}, 1.1/typemax(UInt64)*2^61) == eps(UFixed{UInt64,3})
71+
@test convert(UFixed{UInt64,3}, 1.1/typemax(UInt64)*UInt64(2)^61) == eps(UFixed{UInt64,3})
6872
@test convert(UFixed{UInt128,7}, 1.1/typemax(UInt128)*UInt128(2)^121) == eps(UFixed{UInt128,7})
6973

7074
@test convert(UFixed8, 1.1f0/typemax(UInt8)) == eps(UFixed8)
@@ -93,15 +97,18 @@ end
9397
@test (65.2 % UFixed10).i == round(Int, 65.2*1023) % UInt16
9498
@test (-0.3 % UFixed10).i == round(Int, -0.3*1023) % UInt16
9599

100+
@test 1 % UFixed8 == 1
101+
@test 2 % UFixed8 == UFixed8(0.996)
102+
96103
x = UFixed8(0b01010001, 0)
97104
@test ~x == UFixed8(0b10101110, 0)
98105
@test -x == 0xafuf8
99106

100107
for T in (FixedPointNumbers.UF..., UF2...)
101108
x = T(0x10,0)
102109
y = T(0x25,0)
103-
fx = convert(Float32, x)
104-
fy = convert(Float32, y)
110+
fx = convert(FixedPointNumbers.floattype(T), x)
111+
fy = convert(FixedPointNumbers.floattype(T), y)
105112
@test y > x
106113
@test y != x
107114
@test typeof(x+y) == T
@@ -154,6 +161,9 @@ for T in (FixedPointNumbers.UF..., UF2...)
154161
testtrunc(eps(T))
155162
end
156163

164+
@test !(UFixed8(0.5) < UFixed8(0.5))
165+
@test UFixed8(0.5) <= UFixed8(0.5)
166+
157167
@test div(0x10uf8, 0x02uf8) == fld(0x10uf8, 0x02uf8) == 8
158168
@test div(0x0fuf8, 0x02uf8) == fld(0x0fuf8, 0x02uf8) == 7
159169
@test Base.fld1(0x10uf8, 0x02uf8) == 8
@@ -166,6 +176,14 @@ end
166176
r = 1uf8:1uf8:48uf8
167177
@test length(r) == 48
168178

179+
# Promotion within UFixed
180+
@test @inferred(promote(UFixed8(0.2), UFixed8(0.8))) ===
181+
(UFixed8(0.2), UFixed8(0.8))
182+
@test @inferred(promote(UFixed{UInt16,3}(0.2), UFixed{UInt8,3}(0.86))) ===
183+
(UFixed{UInt16,3}(0.2), UFixed{UInt16,3}(0.86))
184+
@test @inferred(promote(UFixed{UInt8,7}(0.197), UFixed{UInt8,4}(0.8))) ===
185+
(UFixed{UInt16,7}(0.197), UFixed{UInt16,7}(0.8))
186+
169187
# Show
170188
x = 0xaauf8
171189
iob = IOBuffer()

0 commit comments

Comments
 (0)