Skip to content

Rename UFixed to Normed #63

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ is the number of fraction bits.

For `T<:Signed` (a signed integer), there is a fixed-point type
`Fixed{T, f}`; for `T<:Unsigned` (an unsigned integer), there is the
`UFixed{T, f}` type. However, there are slight differences in behavior
`Normed{T, f}` type. However, there are slight differences in behavior
that go beyond signed/unsigned distinctions.

The `Fixed{T,f}` types use 1 bit for sign, and `f` bits to represent
Expand All @@ -43,23 +43,23 @@ is interpreted as if the integer representation has been divided by

because the range of `Int8` is from -128 to 127.

In contrast, the `UFixed{T,f}`, with `f` fraction bits, map the closed
In contrast, the `Normed{T,f}`, with `f` fraction bits, map the closed
interval [0.0,1.0] to the span of numbers with `f` bits. For example,
the `UFixed8` type (aliased to `UFixed{UInt8,8}`) is represented
the `Normed8` type (aliased to `Normed{UInt8,8}`) is represented
internally by a `UInt8`, and makes `0x00` equivalent to `0.0` and
`0xff` to `1.0`. Consequently, `UFixed` numbers are scaled by `2^f-1`
rather than `2^f`. The type aliases `UFixed10`, `UFixed12`,
`UFixed14`, and `UFixed16` are all based on `UInt16` and reach the
`0xff` to `1.0`. Consequently, `Normed` numbers are scaled by `2^f-1`
rather than `2^f`. The type aliases `Normed10`, `Normed12`,
`Normed14`, and `Normed16` are all based on `UInt16` and reach the
value `1.0` at 10, 12, 14, and 16 bits, respectively (`0x03ff`,
`0x0fff`, `0x3fff`, and `0xffff`).

To construct such a number, use `convert(UFixed12, 1.3)`, `UFixed12(1.3)`, `UFixed{UInt16,12}(1.3)`, or the literal syntax
`0x14ccuf12`. The latter syntax means to construct a `UFixed12` (it ends in
To construct such a number, use `convert(Normed12, 1.3)`, `Normed12(1.3)`, `Normed{UInt16,12}(1.3)`, or the literal syntax
`0x14ccuf12`. The latter syntax means to construct a `Normed12` (it ends in
`uf12`) from the `UInt16` value `0x14cc`.

More generally, an arbitrary number of bits from any of the standard unsigned
integer widths can be used for the fractional part. For example:
`UFixed{UInt32,16}`, `UFixed{UInt64,3}`, `UFixed{UInt128,7}`.
`Normed{UInt32,16}`, `Normed{UInt64,3}`, `Normed{UInt128,7}`.

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

Expand Down
4 changes: 2 additions & 2 deletions src/FixedPointNumbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ abstract FixedPoint{T <: Integer, f} <: Real
export
FixedPoint,
Fixed,
UFixed,
Normed,
# "special" typealiases
# Q and U typealiases are exported in separate source files
# literal constructor constants
Expand Down Expand Up @@ -112,7 +112,7 @@ showcompact{T,f}(io::IO, x::FixedPoint{T,f}) = show(io, round(convert(Float64,x)


include("fixed.jl")
include("ufixed.jl")
include("normed.jl")
include("deprecations.jl")

eps{T<:FixedPoint}(::Type{T}) = T(one(rawtype(T)),0)
Expand Down
25 changes: 14 additions & 11 deletions src/deprecations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import Base.@deprecate_binding
@deprecate_binding UFixed14 N2f14
@deprecate_binding UFixed16 N0f16

@deprecate_binding UfixedBase UFixed
@deprecate_binding Ufixed UFixed
@deprecate_binding UfixedBase Normed
@deprecate_binding Ufixed Normed
@deprecate_binding UFixed Normed
@deprecate_binding Ufixed8 N0f8
@deprecate_binding Ufixed10 N6f10
@deprecate_binding Ufixed12 N4f12
Expand All @@ -35,20 +36,22 @@ Compat.@dep_vectorize_1arg Real ufixed16

## The next lines mimic the floating-point literal syntax "3.2f0"
# construction using a UInt, i.e., 0xccuf8
immutable UFixedConstructor{T,f} end
function *{T,f}(n::Integer, ::UFixedConstructor{T,f})
immutable NormedConstructor{T,f} end
function *{T,f}(n::Integer, ::NormedConstructor{T,f})
i = 8*sizeof(T)-f
io = IOBuffer()
show(io, n)
nstr = takebuf_string(io)
cstr = typeof(n) == T ? nstr : "convert($T, $nstr)"
Base.depwarn("$(nstr)uf$f is deprecated, please use reinterpret(N$(i)f$f, $cstr) instead", :*)
reinterpret(UFixed{T,f}, convert(T, n))
reinterpret(Normed{T,f}, convert(T, n))
end
const uf8 = UFixedConstructor{UInt8,8}()
const uf10 = UFixedConstructor{UInt16,10}()
const uf12 = UFixedConstructor{UInt16,12}()
const uf14 = UFixedConstructor{UInt16,14}()
const uf16 = UFixedConstructor{UInt16,16}()
const uf8 = NormedConstructor{UInt8,8}()
const uf10 = NormedConstructor{UInt16,10}()
const uf12 = NormedConstructor{UInt16,12}()
const uf14 = NormedConstructor{UInt16,14}()
const uf16 = NormedConstructor{UInt16,16}()

@deprecate_binding UfixedConstructor NormedConstructor
@deprecate_binding UFixedConstructor NormedConstructor

@deprecate_binding UfixedConstructor UFixedConstructor
161 changes: 161 additions & 0 deletions src/normed.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Normed{T,f} maps UInts from 0 to 2^f-1 to the range [0.0, 1.0]
# For example, Normed{UInt8,8} == N0f8 maps 0x00 to 0.0 and 0xff to 1.0

immutable Normed{T<:Unsigned,f} <: FixedPoint{T,f}
i::T

Normed(i::Integer,_) = new(i%T) # for setting by raw representation
Normed(x) = convert(Normed{T,f}, x)
end

rawtype{T,f}(::Type{Normed{T,f}}) = T
rawtype(x::Number) = rawtype(typeof(x))
nbitsfrac{T,f}(::Type{Normed{T,f}}) = f
floattype{T<:Normed}(::Type{T}) = floattype(supertype(T))
typechar{X<:Normed}(::Type{X}) = 'N'
signbits{X<:Normed}(::Type{X}) = 0

for T in (UInt8, UInt16, UInt32, UInt64)
for f in 0:sizeof(T)*8
sym = Symbol(takebuf_string(showtype(_iotypealias, Normed{T,f})))
@eval begin
typealias $sym Normed{$T,$f}
export $sym
end
end
end

reinterpret{T<:Unsigned, f}(::Type{Normed{T,f}}, x::T) = Normed{T,f}(x, 0)

zero{T,f}(::Type{Normed{T,f}}) = Normed{T,f}(zero(T),0)
function one{T<:Normed}(::Type{T})
T(typemax(rawtype(T)) >> (8*sizeof(T)-nbitsfrac(T)), 0)
end
zero(x::Normed) = zero(typeof(x))
one(x::Normed) = one(typeof(x))
rawone(v) = reinterpret(one(v))

# Conversions
convert{T<:Normed}(::Type{T}, x::T) = x
convert{T1,T2,f}(::Type{Normed{T1,f}}, x::Normed{T2,f}) = Normed{T1,f}(convert(T1, x.i), 0)
function convert{T,T2,f}(::Type{Normed{T,f}}, x::Normed{T2})
U = Normed{T,f}
y = round((rawone(U)/rawone(x))*reinterpret(x))
(0 <= y) & (y <= typemax(T)) || throw_converterror(U, x)
reinterpret(U, _unsafe_trunc(T, y))
end
convert(::Type{N0f16}, x::N0f8) = reinterpret(N0f16, convert(UInt16, 0x0101*reinterpret(x)))
convert{U<:Normed}(::Type{U}, x::Real) = _convert(U, rawtype(U), x)
function _convert{U<:Normed,T}(::Type{U}, ::Type{T}, x)
y = round(widen1(rawone(U))*x)
(0 <= y) & (y <= typemax(T)) || throw_converterror(U, x)
U(_unsafe_trunc(T, y), 0)
end
function _convert{U<:Normed}(::Type{U}, ::Type{UInt128}, x)
y = round(rawone(U)*x) # for UInt128, we can't widen
(0 <= y) & (y <= typemax(UInt128)) & (x <= Float64(typemax(U))) || throw_converterror(U, x)
U(_unsafe_trunc(UInt128, y), 0)
end

rem{T<:Normed}(x::T, ::Type{T}) = x
rem{T<:Normed}(x::Normed, ::Type{T}) = reinterpret(T, _unsafe_trunc(rawtype(T), round((rawone(T)/rawone(x))*reinterpret(x))))
rem{T<:Normed}(x::Real, ::Type{T}) = reinterpret(T, _unsafe_trunc(rawtype(T), round(rawone(T)*x)))

# convert(::Type{AbstractFloat}, x::Normed) = convert(floattype(x), x)
float(x::Normed) = convert(floattype(x), x)

convert(::Type{BigFloat}, x::Normed) = reinterpret(x)*(1/BigFloat(rawone(x)))
function convert{T<:AbstractFloat}(::Type{T}, x::Normed)
y = reinterpret(x)*(one(rawtype(x))/convert(T, rawone(x)))
convert(T, y) # needed for types like Float16 which promote arithmetic to Float32
end
convert(::Type{Bool}, x::Normed) = x == zero(x) ? false : true
convert{T<:Integer}(::Type{T}, x::Normed) = convert(T, x*(1/one(T)))
convert{Ti<:Integer}(::Type{Rational{Ti}}, x::Normed) = convert(Ti, reinterpret(x))//convert(Ti, rawone(x))
convert(::Type{Rational}, x::Normed) = reinterpret(x)//rawone(x)

# Traits
abs(x::Normed) = x

(-){T<:Normed}(x::T) = T(-reinterpret(x), 0)
(~){T<:Normed}(x::T) = T(~reinterpret(x), 0)

+{T,f}(x::Normed{T,f}, y::Normed{T,f}) = Normed{T,f}(convert(T, x.i+y.i),0)
-{T,f}(x::Normed{T,f}, y::Normed{T,f}) = Normed{T,f}(convert(T, x.i-y.i),0)
*{T<:Normed}(x::T, y::T) = convert(T,convert(floattype(T), x)*convert(floattype(T), y))
/{T<:Normed}(x::T, y::T) = convert(T,convert(floattype(T), x)/convert(floattype(T), y))

# Comparisons
<{T<:Normed}(x::T, y::T) = reinterpret(x) < reinterpret(y)
<={T<:Normed}(x::T, y::T) = reinterpret(x) <= reinterpret(y)

# Functions
trunc{T<:Normed}(x::T) = T(div(reinterpret(x), rawone(T))*rawone(T),0)
floor{T<:Normed}(x::T) = trunc(x)
function round{T,f}(x::Normed{T,f})
mask = convert(T, 1<<(f-1))
y = trunc(x)
return convert(T, reinterpret(x)-reinterpret(y)) & mask>0 ?
Normed{T,f}(y+one(Normed{T,f})) : y
end
function ceil{T,f}(x::Normed{T,f})
k = 8*sizeof(T)-f
mask = (typemax(T)<<k)>>k
y = trunc(x)
return convert(T, reinterpret(x)-reinterpret(y)) & (mask)>0 ?
Normed{T,f}(y+one(Normed{T,f})) : y
end

trunc{T<:Integer}(::Type{T}, x::Normed) = convert(T, div(reinterpret(x), rawone(x)))
round{T<:Integer}(::Type{T}, x::Normed) = round(T, reinterpret(x)/rawone(x))
floor{T<:Integer}(::Type{T}, x::Normed) = trunc(T, x)
ceil{T<:Integer}(::Type{T}, x::Normed) = ceil(T, reinterpret(x)/rawone(x))

isfinite(x::Normed) = true
isnan(x::Normed) = false
isinf(x::Normed) = false

bswap{f}(x::Normed{UInt8,f}) = x
bswap(x::Normed) = typeof(x)(bswap(reinterpret(x)),0)

function minmax{T<:Normed}(x::T, y::T)
a, b = minmax(reinterpret(x), reinterpret(y))
T(a,0), T(b,0)
end

# Iteration
# The main subtlety here is that iterating over 0x00uf8:0xffuf8 will wrap around
# unless we iterate using a wider type
@inline start{T<:Normed}(r::StepRange{T}) = widen1(reinterpret(r.start))
@inline next{T<:Normed}(r::StepRange{T}, i::Integer) = (T(i,0), i+reinterpret(r.step))
@inline function done{T<:Normed}(r::StepRange{T}, i::Integer)
i1, i2 = reinterpret(r.start), reinterpret(r.stop)
isempty(r) | (i < min(i1, i2)) | (i > max(i1, i2))
end

function decompose(x::Normed)
g = gcd(reinterpret(x), rawone(x))
div(reinterpret(x),g), 0, div(rawone(x),g)
end

# Promotions
promote_rule{T<:Normed,Tf<:AbstractFloat}(::Type{T}, ::Type{Tf}) = promote_type(floattype(T), Tf)
promote_rule{T<:Normed, R<:Rational}(::Type{T}, ::Type{R}) = R
function promote_rule{T<:Normed, Ti<:Union{Signed,Unsigned}}(::Type{T}, ::Type{Ti})
floattype(T)
end
@generated function promote_rule{T1,T2,f1,f2}(::Type{Normed{T1,f1}}, ::Type{Normed{T2,f2}})
f = max(f1, f2) # ensure we have enough precision
T = promote_type(T1, T2)
# make sure we have enough integer bits
i1, i2 = 8*sizeof(T1)-f1, 8*sizeof(T2)-f2 # number of integer bits for each
i = 8*sizeof(T)-f
while i < max(i1, i2)
T = widen1(T)
i = 8*sizeof(T)-f
end
:(Normed{$T,$f})
end

_unsafe_trunc{T}(::Type{T}, x::Integer) = x % T
_unsafe_trunc{T}(::Type{T}, x) = unsafe_trunc(T, x)
Loading