Skip to content

Empower Fixed and demotes UFixed. #35

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

Closed
wants to merge 7 commits into from
Closed
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
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ Fixed-point numbers can be used to perform arithmetic. Another practical
application is to implicitly rescale integers without modifying the
underlying representation.

This library exports two categories of fixed-point types. Fixed-point types are
used like any other number: they can be added, multiplied, raised to a power,
etc. In many cases these operations result in conversion to floating-point types.
This library exports two categories of fixed-point types.
Fixed-point types are used like any other number: they can be added, multiplied, raised to a power,
etc.

# Type hierarchy
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.

For signed integers, there is a fixed-point type `Fixed{T, f}` and for unsigned integers, there is the `UFixed{T, f}` type.
For signed and unsigned integers, there is a fixed-point type `Fixed{T, f}`.

These types, built with `f` fraction bits, map the closed interval [0.0,1.0]
to the span of numbers with `f` bits.
The type `UFixed` provides an implementation for the special case,
where one wants to map the closed interval [0.0,1.0] to the span of numbers with `f` bits.
For example, the `UFixed8` type is represented internally by a `UInt8`, and makes
`0x00` equivalent to `0.0` and `0xff` to `1.0`.
The types `UFixed10`, `UFixed12`, `UFixed14`, and `UFixed16` are all based on `UInt16`
Expand All @@ -31,6 +31,6 @@ To construct such a number, use `convert(UFixed12, 1.3)`, `ufixed12(1.3)`, or th
The latter syntax means to construct a `UFixed12` (it ends in `uf12`) from the `UInt16` value
`0x14cc`.

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

[wikipedia]: http://en.wikipedia.org/wiki/Fixed-point_arithmetic
86 changes: 60 additions & 26 deletions src/FixedPointNumbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ using Base: IdFun, AddFun, MulFun, reducedim_initarray
import Base: ==, <, <=, -, +, *, /, ~,
convert, promote_rule, show, showcompact, isinteger, abs, decompose,
isnan, isinf, isfinite,
zero, one, typemin, typemax, realmin, realmax, eps, sizeof, reinterpret,
zero, one, typemin, typemax, realmin, realmax, eps, sizeof, reinterpret, getindex,
trunc, round, floor, ceil, bswap,
div, fld, rem, mod, mod1, rem1, fld1, min, max,
start, next, done, r_promote, reducedim_init
# T => BaseType
# f => Number of Bytes reserved for fractional part
abstract FixedPoint{T <: Integer, f} <: Real
abstract AbstractFixedPoint{T <: Integer, f} <: Real

export
AbstractFixedPoint,
FixedPoint,
Fixed,
UFixed,
Expand All @@ -40,54 +41,87 @@ export
# Functions
scaledual

reinterpret(x::FixedPoint) = x.i
getindex(x::AbstractFixedPoint) = x.i
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting, but I worry it might give people the wrong idea that there's some kind of tuple or array container inside. So I'm on the fence about it. Care to provide a little explanation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When going through the codebase I found that reinterpret(x) and x.i where both used, thus defeating the point of using reinterpret(x), also for me personally reinterpret makes it harder to read or quickly parse the code. So my main issue with it is that it is to long and it gets in the way. I was thinking about using get(x), but it is semantically as problematic as getindex.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem, however, with your solution is that it conflicts with

julia> 5[]
5

julia> 3.2[]
3.2

So anyone who writes code like

function mysum(X)
    s = zero(first(X))
    for x in X
        s += x
    end
    s
end

will get a very surprising answer from mysum(0xffuf8).

x.i might be used inside FixedPointNumbers, but I've never seen such usage outside. If this is intended only for internal use, rather than overload an exported function from Base you could define raw (or something) as an internal function.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point, I will change it to raw.

On Tue, 3 Nov 2015 18:33 Tim Holy [email protected] wrote:

In src/FixedPointNumbers.jl
#35 (comment)
:

@@ -40,54 +41,87 @@ export
# Functions
scaledual

-reinterpret(x::FixedPoint) = x.i
+getindex(x::AbstractFixedPoint) = x.i

The problem, however, with your solution is that it conflicts with

julia> 5[]5

julia> 3.2[]3.2

So anyone who writes code like

function mysum(X)
s = zero(first(X))
for x in X
s += x
end
s

will get a very surprising answer from mysum(0xffuf8).

x.i might be used inside FixedPointNumbers, but I've never seen such
usage outside. If this is intended only for internal use, rather than
overload an exported function from Base you could define raw (or
something) as an internal function.


Reply to this email directly or view it on GitHub
https://github.com/JeffBezanson/FixedPointNumbers.jl/pull/35/files#r43728468
.

reinterpret{T,f}(::Type{T}, x::AbstractFixedPoint{T,f}) = x[]
# reinterpret{T <: Integer,f}(::Type{AbstractFixedPoint{T,f}}, x::T) = AbstractFixedPoint{T,f}(x, 0)


# comparison
=={T <: FixedPoint}(x::T, y::T) = x.i == y.i
<{T <: FixedPoint}(x::T, y::T) = x.i < y.i
<={T <: FixedPoint}(x::T, y::T) = x.i <= y.i
=={T <: AbstractFixedPoint}(x::T, y::T) = x[] == y[]
<{T <: AbstractFixedPoint}(x::T, y::T) = x[] < y[]
<={T <: AbstractFixedPoint}(x::T, y::T) = x[] <= y[]

#predicates
isfinite(x::AbstractFixedPoint) = true
isnan(x::AbstractFixedPoint) = false
isinf(x::AbstractFixedPoint) = false

#traits
typemax{T<: AbstractFixedPoint}(::Type{T}) = T(typemax(rawtype(T)), 0)
typemin{T<: AbstractFixedPoint}(::Type{T}) = T(typemin(rawtype(T)), 0)
realmin{T<: AbstractFixedPoint}(::Type{T}) = typemin(T)
realmax{T<: AbstractFixedPoint}(::Type{T}) = typemax(T)
eps{T<:AbstractFixedPoint}(::Type{T}) = T(one(rawtype(T)),0)
eps{T<:AbstractFixedPoint}(::T) = eps(T)
sizeof{T<:AbstractFixedPoint}(::Type{T}) = sizeof(rawtype(T))

# predicates
isinteger{T,f}(x::FixedPoint{T,f}) = (x.i&(1<<f-1)) == 0
zero{T <: AbstractFixedPoint}(::Type{T}) = T(zero(rawtype(T)),0)
zero{T <: AbstractFixedPoint}(x::T) = zero(T)
one{T <: AbstractFixedPoint}(x::T) = one(T)

typemax{T<: FixedPoint}(::Type{T}) = T(typemax(rawtype(T)), 0)
typemin{T<: FixedPoint}(::Type{T}) = T(typemin(rawtype(T)), 0)
realmin{T<: FixedPoint}(::Type{T}) = typemin(T)
realmax{T<: FixedPoint}(::Type{T}) = typemax(T)
# Basic operators & arithmetics
(-){T<:AbstractFixedPoint}(x::T) = T(-x[], 0)
(~){T<:AbstractFixedPoint}(x::T) = T(~x[], 0)
abs{T<:AbstractFixedPoint}(x::T) = T(abs(x[]),0)

+{T<:AbstractFixedPoint}(x::T, y::T) = T(x[] + y[],0)
-{T<:AbstractFixedPoint}(x::T, y::T) = T(x[] - y[],0)

for f in (:div, :fld, :rem, :mod, :mod1, :rem1, :fld1, :min, :max)
@eval begin
$f{T<:AbstractFixedPoint}(x::T, y::T) = T($f(x[],y[]),0)
end
end

include("fixed.jl")
function minmax{T<:AbstractFixedPoint}(x::T, y::T)
a, b = minmax(x[], y[])
T(a,0), T(b,0)
end

bswap{T <: Union{UInt8, Int8}, f}(x::AbstractFixedPoint{T,f}) = x
bswap{T <: AbstractFixedPoint}(x::T) = T(bswap(x[]),0)

include("fixedpoint.jl")
include("ufixed.jl")
include("deprecations.jl")


# Promotions for reductions
const Treduce = Float64
for F in (AddFun, MulFun)
@eval r_promote{T}(::$F, x::FixedPoint{T}) = Treduce(x)
@eval r_promote{T}(::$F, x::AbstractFixedPoint{T}) = Treduce(x)
end

reducedim_init{T<:FixedPoint}(f::IdFun, op::AddFun,
reducedim_init{T<:AbstractFixedPoint}(f::IdFun, op::AddFun,
A::AbstractArray{T}, region) =
reducedim_initarray(A, region, zero(Treduce))
reducedim_init{T<:FixedPoint}(f::IdFun, op::MulFun,
reducedim_init{T<:AbstractFixedPoint}(f::IdFun, op::MulFun,
A::AbstractArray{T}, region) =
reducedim_initarray(A, region, one(Treduce))

# TODO: rewrite this by @generated
for T in tuple(Fixed16, UF...)
R = rawtype(T)
@eval begin
reinterpret(::Type{$R}, x::$T) = x.i
end
end
# Iteration
# The main subtlety here is that iterating over 0x00uf8:0xffuf8 will wrap around
# unless we iterate using a wider type
start{T<:AbstractFixedPoint}(r::StepRange{T}) = convert(typeof(r.start[] + r.step[]), r.start[])
next{T<:AbstractFixedPoint}(r::StepRange{T}, i::Integer) = (T(i,0), i+r.step[])
done{T<:AbstractFixedPoint}(r::StepRange{T}, i::Integer) = isempty(r) || (i > r.stop[])

# When multiplying by a float, reduce two multiplies to one.
# Particularly useful for arrays.
scaledual(Tdual::Type, x) = one(Tdual), x
scaledual{Tdual<:Number}(b::Tdual, x) = b, x
scaledual{T<:FixedPoint}(Tdual::Type, x::Union{T,AbstractArray{T}}) =
scaledual{T<:AbstractFixedPoint}(Tdual::Type, x::Union{T,AbstractArray{T}}) =
convert(Tdual, 1/one(T)), reinterpret(rawtype(T), x)
scaledual{Tdual<:Number, T<:FixedPoint}(b::Tdual, x::Union{T,AbstractArray{T}}) =
scaledual{Tdual<:Number, T<:AbstractFixedPoint}(b::Tdual, x::Union{T,AbstractArray{T}}) =
convert(Tdual, b/one(T)), reinterpret(rawtype(T), x)

end # module
2 changes: 1 addition & 1 deletion src/deprecations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ import Base.@deprecate_binding
@deprecate_binding Ufixed16 UFixed16

@deprecate_binding Fixed32 Fixed16
@deprecate Fixed(x::Real) convert(Fixed{Int32, 16}, x)
@deprecate reinterpret(x::AbstractFixedPoint) getindex(x)
66 changes: 0 additions & 66 deletions src/fixed.jl

This file was deleted.

127 changes: 127 additions & 0 deletions src/fixedpoint.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
###
# FixedPoint implements a general purpose FixedPoint number.
# The underlying storage type is given by the parameter `T`,
# and the number of fractional bits is given by `f`
# The dot is just before the fractional part so in order to represent one,
# `f` needs to be atleast smaller by 1 in the unsigned case and smaller by two
# in the signed case, then the number of bits in `T`.
#
# In a further iteration of the design `FixedPoint` should be renamed to `FixedPointPoint` and the aliase `typealias FixedPoint{T <: Signed, f} FixedPointPoint{T, f}` and `typealias UFixedPoint{T <: Unsigned, f} FixedPointPoint{T, f}` introduced.
###
immutable FixedPoint{T,f} <: AbstractFixedPoint{T, f}
i::T

# constructor for manipulating the representation;
# selected by passing an extra dummy argument
FixedPoint(i::T, _) = new(i)
FixedPoint(i::Integer,_) = new(i % T)

FixedPoint(x) = convert(FixedPoint{T,f}, x)
end

typealias Fixed{T <: Signed, f} FixedPoint{T, f}
typealias Fixed16 Fixed{Int32, 16}

rawtype{T,f}(::Type{FixedPoint{T,f}}) = T
nbitsfrac{T,f}(::Type{FixedPoint{T,f}}) = f

reinterpret{T <: Integer,f}(::Type{FixedPoint{T,f}}, x::T) = FixedPoint{T,f}(x, 0)

## predicates
isinteger{T,f}(x::FixedPoint{T,f}) = (x[]&(1<<f-1)) == 0

function one{T, f}(::Type{FixedPoint{T, f}})
if T <: Unsigned && sizeof(T) * 8 > f ||
T <: Signed && sizeof(T) * 8 > (f+1)
return FixedPoint{T, f}(2^f-1,0)
else
throw(DomainError())
end
end

## basic operators & arithmetics

# with truncation:
# *{T<:FixedPoint}(x::T, y::T) =
# T(Base.widemul(x[],y[]) >> nbitsfrac(T),0)
# with rounding up:
function *{T<:FixedPoint}(x::T, y::T)
f = nbitsfrac(T)
i = Base.widemul(x[],y[])
T((i + convert(widen(rawtype(T)), 1) << (f-1) )>>f,0)
end

function /{T<:FixedPoint}(x::T, y::T)
f = nbitsfrac(T)
T(div(convert(widen(rawtype(T)), x[]) << f, y.i), 0)
end

## rounding
# Round towards negative infinity
trunc{T<:FixedPoint}(x::T) = T(x[] & ~(1 << nbitsfrac(T) - 1), 0)
# Round towards negative infinity
floor{T<:FixedPoint}(x::T) = trunc(x)
# Round towards positive infinity
ceil{T<:FixedPoint}(x::T) = trunc(T(x[] + 1 << (nbitsfrac(T)-1), 0))
# Round towards even
function round{T<:FixedPoint}(x::T)
even = x[] & (1 << nbitsfrac(T)) == 0
if even
return floor(x)
else
return ceil(x)
end
end
# Round towards positive infinity
ceil{T<:FixedPoint}(x::T) = trunc(T(x[] + 1 << (nbitsfrac(T)-1), 0))
# Round towards even
function round{T<:FixedPoint}(x::T)
even = x[] & (1 << nbitsfrac(T)) == 0
if even
return floor(x)
else
return ceil(x)
end
end

trunc{TI<:Integer, T <: FixedPoint}(::Type{TI}, x::T) =
convert(TI, x[] >> nbitsfrac(T))
floor{T<:Integer}(::Type{T}, x::FixedPoint) = trunc(T, x)
ceil{T<:Integer}(::Type{T}, x::FixedPoint) = trunc(T, ceil(x))
round{T<:Integer}(::Type{T}, x::FixedPoint) = trunc(T, round(x))


## conversions and promotions
convert{T,f}(::Type{FixedPoint{T,f}}, x::Integer) = FixedPoint{T,f}(convert(T,x)<<f,0)
convert{T,f}(::Type{FixedPoint{T,f}}, x::AbstractFloat) = FixedPoint{T,f}(trunc(T,x)<<f + round(T, rem(x,1)*(1<<f)),0)
convert{T,f}(::Type{FixedPoint{T,f}}, x::Rational) = FixedPoint{T,f}(x.num)/FixedPoint{T,f}(x.den)

convert{T,f}(::Type{BigFloat}, x::FixedPoint{T,f}) =
convert(BigFloat,x[]>>f) + convert(BigFloat,x[]&(1<<f - 1))/convert(BigFloat,1<<f)
convert{TF<:AbstractFloat,T,f}(::Type{TF}, x::FixedPoint{T,f}) =
convert(TF,x[]>>f) + convert(TF,x[]&(1<<f - 1))/convert(TF,1<<f)

convert{T,f}(::Type{Bool}, x::FixedPoint{T,f}) = x[]!=0
function convert{TI<:Integer, T,f}(::Type{TI}, x::FixedPoint{T,f})
isinteger(x) || throw(InexactError())
convert(TI, x[]>>f)
end

convert{TR<:Rational,T,f}(::Type{TR}, x::FixedPoint{T,f}) =
convert(TR, x[]>>f + (x[]&(1<<f-1))//(1<<f))

promote_rule{T,f,TI<:Integer}(ft::Type{FixedPoint{T,f}}, ::Type{TI}) = FixedPoint{T,f}
promote_rule{T,f,TF<:AbstractFloat}(::Type{FixedPoint{T,f}}, ::Type{TF}) = TF
promote_rule{T,f,TR}(::Type{FixedPoint{T,f}}, ::Type{Rational{TR}}) = Rational{TR}

decompose{T,f}(x::FixedPoint{T,f}) = x[], -f, 1

# printing
function show(io::IO, x::FixedPoint)
print(io, typeof(x))
print(io, "(")
showcompact(io, x)
print(io, ")")
end
const _log2_10 = 3.321928094887362
showcompact{T,f}(io::IO, x::FixedPoint{T,f}) = show(io, round(convert(Float64,x), ceil(Int,f/_log2_10)))
Loading