Skip to content

Commit f1b4f2a

Browse files
committed
added support for an arbitrary number of fraction bits
1 parent 3964895 commit f1b4f2a

File tree

5 files changed

+70
-76
lines changed

5 files changed

+70
-76
lines changed

README.md

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,29 @@ used like any other number: they can be added, multiplied, raised to a power,
1515
etc. In many cases these operations result in conversion to floating-point types.
1616

1717
# Type hierarchy
18-
This library defines an abstract type `FixedPoint{T <: Integer, f}` as a subtype of `Real`. The parameter `T` is the underlying representation and `f` is the number of fraction bits.
19-
20-
For signed integers, there is a fixed-point type `Fixed{T, f}` and for unsigned integers, there is the `UFixed{T, f}` type.
21-
22-
These types, built with `f` fraction bits, map the closed interval [0.0,1.0]
23-
to the span of numbers with `f` bits.
24-
For example, the `UFixed8` type is represented internally by a `UInt8`, and makes
25-
`0x00` equivalent to `0.0` and `0xff` to `1.0`.
26-
The types `UFixed10`, `UFixed12`, `UFixed14`, and `UFixed16` are all based on `UInt16`
27-
and reach the value `1.0` at 10, 12, 14, and 16 bits, respectively (`0x03ff`, `0x0fff`,
28-
`0x3fff`, and `0xffff`).
29-
30-
To construct such a number, use `convert(UFixed12, 1.3)`, `ufixed12(1.3)`, or the literal syntax `0x14ccuf12`.
31-
The latter syntax means to construct a `UFixed12` (it ends in `uf12`) from the `UInt16` value
32-
`0x14cc`.
18+
This library defines an abstract type `FixedPoint{T <: Integer, f}` as a
19+
subtype of `Real`. The parameter `T` is the underlying representation and `f`
20+
is the number of fraction bits.
21+
22+
For signed integers, there is a fixed-point type `Fixed{T, f}` and for unsigned
23+
integers, there is the `UFixed{T, f}` type.
24+
25+
These types, built with `f` fraction bits, map the closed interval [0.0,1.0] to
26+
the span of numbers with `f` bits. For example, the `UFixed8` type (aliased to
27+
UFixed{UInt8,8}) is represented internally by a `UInt8`, and makes `0x00`
28+
equivalent to `0.0` and `0xff` to `1.0`. The type aliases `UFixed10`, `UFixed12`,
29+
`UFixed14`, and `UFixed16` are all based on `UInt16` and reach the value `1.0`
30+
at 10, 12, 14, and 16 bits, respectively (`0x03ff`, `0x0fff`, `0x3fff`, and
31+
`0xffff`).
32+
33+
To construct such a number, use `convert(UFixed12, 1.3)`, `ufixed12(1.3)` (a
34+
convenience function), `UFixed{UInt16,12}`, or the literal syntax `0x14ccuf12`.
35+
The latter syntax means to construct a `UFixed12` (it ends in `uf12`) from the
36+
`UInt16` value `0x14cc`.
37+
38+
More generally, an arbitrary number of bits from any of the standard unsigned
39+
integer widths can be used for the fractional part. For example:
40+
`UFixed{UInt32,16}`, `UFixed{UInt64,3}`, `UFixed{UInt128,7}`.
3341

3442
There currently is no literal syntax for signed `Fixed` numbers.
3543

src/FixedPointNumbers.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,14 @@ scaledual{T<:FixedPoint}(Tdual::Type, x::Union{T,AbstractArray{T}}) =
9090
scaledual{Tdual<:Number, T<:FixedPoint}(b::Tdual, x::Union{T,AbstractArray{T}}) =
9191
convert(Tdual, b/one(T)), reinterpret(rawtype(T), x)
9292

93+
# Show
94+
function show{T<:FixedPoint}(io::IO, x::T)
95+
print(io, typeof(x))
96+
print(io, "(")
97+
showcompact(io, x)
98+
print(io, ")")
99+
end
100+
const _log2_10 = 3.321928094887362
101+
showcompact{T,f}(io::IO, x::UFixed{T,f}) = show(io, round(convert(Float64,x), ceil(Int,f/_log2_10)))
102+
93103
end # module

src/fixed.jl

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,3 @@ promote_rule{T,f,TR}(::Type{Fixed{T,f}}, ::Type{Rational{TR}}) = Rational{TR}
5454

5555
# TODO: Document and check that it still does the right thing.
5656
decompose{T,f}(x::Fixed{T,f}) = x.i, -f, 1
57-
58-
# printing
59-
function show(io::IO, x::Fixed)
60-
print(io, typeof(x))
61-
print(io, "(")
62-
showcompact(io, x)
63-
print(io, ")")
64-
end
65-
const _log2_10 = 3.321928094887362
66-
showcompact{T,f}(io::IO, x::Fixed{T,f}) = show(io, round(convert(Float64,x), ceil(Int,f/_log2_10)))

src/ufixed.jl

Lines changed: 22 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,7 @@ typealias UFixed16 UFixed{UInt16,16}
1919

2020
const UF = (UFixed8, UFixed10, UFixed12, UFixed14, UFixed16)
2121

22-
for (uf) in UF
23-
T = rawtype(uf)
24-
f = nbitsfrac(uf)
25-
@eval reinterpret(::Type{UFixed{$T,$f}}, x::$T) = UFixed{$T,$f}(x, 0)
26-
end
22+
reinterpret{T<:Unsigned, f}(::Type{UFixed{T,f}}, x::T) = UFixed{T,f}(x, 0)
2723

2824
# The next lines mimic the floating-point literal syntax "3.2f0"
2925
immutable UFixedConstructor{T,f} end
@@ -35,14 +31,7 @@ const uf14 = UFixedConstructor{UInt16,14}()
3531
const uf16 = UFixedConstructor{UInt16,16}()
3632

3733
zero{T,f}(::Type{UFixed{T,f}}) = UFixed{T,f}(zero(T),0)
38-
for uf in UF
39-
TT = rawtype(uf)
40-
f = nbitsfrac(uf)
41-
T = UFixed{TT,f}
42-
@eval begin
43-
one(::Type{$T}) = $T($(2^f-1),0)
44-
end
45-
end
34+
one{T<:UFixed}(::Type{T}) = T((2^nbitsfrac(T)-1),0)
4635
zero(x::UFixed) = zero(typeof(x))
4736
one(x::UFixed) = one(typeof(x))
4837
rawone(v) = reinterpret(one(v))
@@ -95,15 +84,21 @@ abs(x::UFixed) = x
9584
# Functions
9685
trunc{T<:UFixed}(x::T) = T(div(reinterpret(x), rawone(T))*rawone(T),0)
9786
floor{T<:UFixed}(x::T) = trunc(x)
98-
for T in UF
99-
f = nbitsfrac(T)
100-
R = rawtype(T)
101-
roundmask = convert(R, 1<<(f-1))
102-
k = 8*sizeof(R)-f
103-
ceilmask = (typemax(R)<<k)>>k
104-
@eval begin
105-
round(x::$T) = (y = trunc(x); return convert(rawtype($T), reinterpret(x)-reinterpret(y))&$roundmask>0 ? $T(y+one($T)) : y)
106-
ceil(x::$T) = (y = trunc(x); return convert(rawtype($T), reinterpret(x)-reinterpret(y))&$ceilmask >0 ? $T(y+one($T)) : y)
87+
@generated function round{T,f}(x::UFixed{T,f})
88+
mask = convert(T, 1<<(f-1))
89+
quote
90+
y = trunc(x)
91+
return convert(T, reinterpret(x)-reinterpret(y)) & $mask>0 ?
92+
UFixed{T,f}(y+one(UFixed{T,f})) : y
93+
end
94+
end
95+
@generated function ceil{T,f}(x::UFixed{T,f})
96+
k = 8*sizeof(T)-f
97+
mask = (typemax(T)<<k)>>k
98+
quote
99+
y = trunc(x)
100+
return convert(T, reinterpret(x)-reinterpret(y)) & ($mask)>0 ?
101+
UFixed{T,f}(y+one(UFixed{T,f})) : y
107102
end
108103
end
109104

@@ -152,25 +147,8 @@ function decompose(x::UFixed)
152147
end
153148

154149
# Promotions
155-
for T in UF
156-
@eval begin
157-
promote_rule(::Type{$T}, ::Type{Float32}) = Float32
158-
promote_rule(::Type{$T}, ::Type{Float64}) = Float64
159-
promote_rule{TR<:Rational}(::Type{$T}, ::Type{TR}) = TR
160-
end
161-
for Ti in (Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64)
162-
Tp = eps(convert(Float32, typemax(Ti))) > eps(T) ? Float64 : Float32
163-
@eval begin
164-
promote_rule(::Type{$T}, ::Type{$Ti}) = $Tp
165-
end
166-
end
167-
end
168-
169-
# Show
170-
function show{T,f}(io::IO, x::UFixed{T,f})
171-
print(io, "UFixed", f)
172-
print(io, "(")
173-
showcompact(io, x)
174-
print(io, ")")
175-
end
176-
showcompact{T,f}(io::IO, x::UFixed{T,f}) = show(io, round(convert(Float64,x), ceil(Int,f/_log2_10)))
150+
promote_rule{T<:UFixed}(::Type{T}, ::Type{Float32}) = Float32
151+
promote_rule{T<:UFixed}(::Type{T}, ::Type{Float64}) = Float64
152+
promote_rule{T<:UFixed, R<:Rational}(::Type{T}, ::Type{R}) = R
153+
promote_rule{T<:UFixed, Ti<:Union{Signed,Unsigned}}(::Type{T}, ::Type{Ti}) =
154+
eps(convert(Float32, typemax(Ti))) > eps(T) ? Float64 : Float32

test/ufixed.jl

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@ using FixedPointNumbers, Base.Test
1717
@test ufixed14(1.0) == 0x3fffuf14
1818
@test ufixed12([2]) == UFixed12[0x1ffeuf12]
1919

20-
for T in FixedPointNumbers.UF
20+
UF2 = (UFixed{UInt32,16}, UFixed{UInt64,3}, UFixed{UInt128,7})
21+
22+
for T in (FixedPointNumbers.UF..., UF2...)
2123
@test zero(T) == 0
2224
@test one(T) == 1
2325
@test one(T) * one(T) == one(T)
2426
@test typemin(T) == 0
2527
@test realmin(T) == 0
2628
@test eps(zero(T)) == eps(typemax(T))
27-
@test sizeof(T) == 1 + (T != UFixed8)
29+
@test sizeof(T) == sizeof(FixedPointNumbers.rawtype(T))
2830
end
2931
@test typemax(UFixed8) == 1
3032
@test typemax(UFixed10) == typemax(UInt16)//(2^10-1)
@@ -34,6 +36,9 @@ end
3436
@test typemax(UFixed10) == typemax(UInt16) // (2^10-1)
3537
@test typemax(UFixed12) == typemax(UInt16) // (2^12-1)
3638
@test typemax(UFixed14) == typemax(UInt16) // (2^14-1)
39+
@test typemax(UFixed{UInt32,16}) == typemax(UInt32) // (2^16-1)
40+
@test typemax(UFixed{UInt64,3}) == typemax(UInt64) // (2^3-1)
41+
@test typemax(UFixed{UInt128,7}) == typemax(UInt128) // (2^7-1)
3742

3843
x = UFixed8(0.5)
3944
@test isfinite(x) == true
@@ -45,13 +50,16 @@ x = UFixed8(0.5)
4550
@test convert(UFixed12, 1.1/typemax(UInt16)*16) == eps(UFixed12)
4651
@test convert(UFixed14, 1.1/typemax(UInt16)*4) == eps(UFixed14)
4752
@test convert(UFixed16, 1.1/typemax(UInt16)) == eps(UFixed16)
53+
@test convert(UFixed{UInt32,16}, 1.1/typemax(UInt32)*2^16) == eps(UFixed{UInt32,16})
54+
@test convert(UFixed{UInt64,3}, 1.1/typemax(UInt64)*2^61) == eps(UFixed{UInt64,3})
55+
@test convert(UFixed{UInt128,7}, 1.1/typemax(UInt128)*UInt128(2)^121) == eps(UFixed{UInt128,7})
4856

4957
@test convert(UFixed8, 1.1f0/typemax(UInt8)) == eps(UFixed8)
5058

5159
@test convert(Float64, eps(UFixed8)) == 1/typemax(UInt8)
5260
@test convert(Float32, eps(UFixed8)) == 1.0f0/typemax(UInt8)
5361
@test convert(BigFloat, eps(UFixed8)) == BigFloat(1)/typemax(UInt8)
54-
for T in FixedPointNumbers.UF
62+
for T in (FixedPointNumbers.UF..., UF2...)
5563
@test convert(Bool, zero(T)) == false
5664
@test convert(Bool, one(T)) == true
5765
@test convert(Bool, convert(T, 0.2)) == true
@@ -64,7 +72,7 @@ x = UFixed8(0b01010001, 0)
6472
@test ~x == UFixed8(0b10101110, 0)
6573
@test -x == 0xafuf8
6674

67-
for T in FixedPointNumbers.UF
75+
for T in (FixedPointNumbers.UF..., UF2...)
6876
x = T(0x10,0)
6977
y = T(0x25,0)
7078
fx = convert(Float32, x)
@@ -89,7 +97,7 @@ function testtrunc{T}(inc::T)
8997
incf = convert(Float64, inc)
9098
tm = reinterpret(typemax(T))/reinterpret(one(T))
9199
x = zero(T)
92-
for i = 0:reinterpret(typemax(T))-1
100+
for i = 0 : min(1e6, reinterpret(typemax(T))-1)
93101
xf = incf*i
94102
try
95103
@test trunc(x) == trunc(xf)
@@ -113,7 +121,7 @@ function testtrunc{T}(inc::T)
113121
end
114122
end
115123

116-
for T in FixedPointNumbers.UF
124+
for T in (FixedPointNumbers.UF..., UF2...)
117125
testtrunc(eps(T))
118126
end
119127

@@ -122,7 +130,7 @@ x = 0xaauf8
122130
iob = IOBuffer()
123131
show(iob, x)
124132
str = takebuf_string(iob)
125-
@test startswith(str, "UFixed8(")
133+
@test startswith(str, "FixedPointNumbers.UFixed{UInt8,8}(")
126134
@test eval(parse(str)) == x
127135

128136
# scaledual

0 commit comments

Comments
 (0)