Skip to content

Compact systematic display of values #51

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 7 commits 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
8 changes: 8 additions & 0 deletions contrib/deprecation_UFixed.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
sed -i "s/UFixed8/N0f8/g" $*
sed -i "s/UFixed10/N6f10/g" $*
sed -i "s/UFixed12/N4f12/g" $*
sed -i "s/UFixed14/N2f14/g" $*
sed -i "s/UFixed16/N0f16/g" $*
# For types from ColorTypes
sed -i "s/U8/N0f8/g" $*
sed -i "s/U16/N0f16/g" $*
59 changes: 30 additions & 29 deletions src/FixedPointNumbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,27 @@ using Compat
# f => Number of Bytes reserved for fractional part
abstract FixedPoint{T <: Integer, f} <: Real


export
FixedPoint,
Fixed,
UFixed,
Fixed16,
UFixed8,
UFixed10,
UFixed12,
UFixed14,
UFixed16,
# literal constructor constants
Normed,
# "special" typealiases
# Q and U typealiases are exported in separate source files
# literal constructor constants
uf8,
uf10,
uf12,
uf14,
uf16,
Copy link
Collaborator

Choose a reason for hiding this comment

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

deprecate uf16 and friends too?

Copy link
Member Author

Choose a reason for hiding this comment

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

We could, but those exist for a different reason---they are "repr constructors"---that's not covered by anything else:

julia> 0xc2uf8
0.761N0f8

julia> 0.761N0f8
0.761N0f8

julia> 0xc2N0f8
ERROR: InexactError()
 in *(::UInt8, ::Type{FixedPointNumbers.UFixed{UInt8,8}}) at /home/tim/.julia/v0.5/FixedPointNumbers/src/FixedPointNumbers.jl:63

Copy link
Collaborator

@bjarthur bjarthur Sep 19, 2016

Choose a reason for hiding this comment

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

i don't think the casual user of julia would find this intuitive. is there nothing to be done that would make that work?

Copy link
Member Author

Choose a reason for hiding this comment

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

If we really don't like these, perhaps we should get rid of them.

But 0xc2N0f8 or N0f8(0xc2) can never work as I think you're intending without causing absolute chaos. See JuliaGraphics/ColorTypes.jl#49 (comment). We need to firmly keep in mind the distinction between value and representation of that value, and generally one should just ignore the representation and think about the number in terms of its value. 0xc2 = 194 is far beyond the reach of a number representable with an N0f8, so if the constructor doesn't throw an error you've got a big problem.

I think the key is to (1) reliably throw errors when the user tries something that's not allowed (we didn't do that before #48 because of arithmetic overflow), and (2) provide better error messages that actually coach users through how to think about this/fix problems, along the lines of #49 (comment).

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, maybe I'll deprecate 0xc2uf8 in favor of reinterpret(N0f8, 0xc2). Reasonable? Forcing people to use reinterpret explicitly might help clarify everyone's thinking.

Copy link
Collaborator

Choose a reason for hiding this comment

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

doh! i was getting confused between value and representation just as you describe.

deprecating uf8 in favor of reinterpret sounds great.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've confused myself so many times I've lost track. But I think we're moving towards a solution that will be easier to keep straight!

# Functions
# Functions
scaledual

reinterpret(x::FixedPoint) = x.i
reinterpret{T,f}(::Type{T}, x::FixedPoint{T,f}) = x.i

# construction using the (approximate) intended value, i.e., N0f8
*{X<:FixedPoint}(x::Real, ::Type{X}) = X(x)

# comparison
=={T <: FixedPoint}(x::T, y::T) = x.i == y.i
Expand Down Expand Up @@ -90,9 +91,28 @@ floattype{T<:ShortInts,f}(::Type{FixedPoint{T,f}}) = Float32
floattype{T,f}(::Type{FixedPoint{T,f}}) = Float64
floattype(x::FixedPoint) = floattype(supertype(typeof(x)))

# This IOBuffer is used during module definition to generate typealias names
_iotypealias = IOBuffer()

# Printing. These are used to generate type-symbols, so we need them
# before we include any files.
function showtype{X<:FixedPoint}(io::IO, ::Type{X})
print(io, typechar(X))
f = nbitsfrac(X)
m = sizeof(X)*8-f-signbits(X)
print(io, m, 'f', f)
io
end
function show{T,f}(io::IO, x::FixedPoint{T,f})
showcompact(io, x)
showtype(io, typeof(x))
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)))


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

eps{T<:FixedPoint}(::Type{T}) = T(one(rawtype(T)),0)
Expand All @@ -113,14 +133,6 @@ reducedim_init{T<:FixedPoint}(f::typeof(@functorize(identity)),
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

for f in (:div, :fld, :fld1)
@eval begin
$f{T<:FixedPoint}(x::T, y::T) = $f(reinterpret(x),reinterpret(y))
Expand All @@ -141,17 +153,6 @@ scaledual{T<:FixedPoint}(Tdual::Type, x::Union{T,AbstractArray{T}}) =
scaledual{Tdual<:Number, T<:FixedPoint}(b::Tdual, x::Union{T,AbstractArray{T}}) =
convert(Tdual, b/one(T)), reinterpret(rawtype(T), x)

# printing
function show{T,f}(io::IO, x::FixedPoint{T,f})
shorttype = typeof(x)<:UFixed ? "UFixed" : "Fixed"
print(io, shorttype, "{", T, ",", f, "}")
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(Float64(x), ceil(Int,f/_log2_10)))

@noinline function throw_converterror{T<:FixedPoint}(::Type{T}, x)
n = 2^(8*sizeof(T))
bitstring = sizeof(T) == 1 ? "an 8-bit" : "a $(8*sizeof(T))-bit"
Expand Down
62 changes: 47 additions & 15 deletions src/deprecations.jl
Original file line number Diff line number Diff line change
@@ -1,25 +1,57 @@
import Base.@deprecate_binding

@deprecate_binding UfixedBase UFixed
@deprecate_binding UfixedConstructor UFixedConstructor
@deprecate_binding Ufixed UFixed
@deprecate_binding Ufixed8 UFixed8
@deprecate_binding Ufixed10 UFixed10
@deprecate_binding Ufixed12 UFixed12
@deprecate_binding Ufixed14 UFixed14
@deprecate_binding Ufixed16 UFixed16

@deprecate_binding Fixed32 Fixed16
@deprecate_binding Fixed16 Q15f16
@deprecate_binding UFixed8 N0f8
@deprecate_binding UFixed10 N6f10
@deprecate_binding UFixed12 N4f12
@deprecate_binding UFixed14 N2f14
@deprecate_binding UFixed16 N0f16

@deprecate_binding UfixedBase Normed
@deprecate_binding Ufixed Normed
@deprecate_binding UFixed Normed
@deprecate_binding Ufixed8 N0f8
@deprecate_binding Ufixed10 N6f10
@deprecate_binding Ufixed12 N4f12
@deprecate_binding Ufixed14 N2f14
@deprecate_binding Ufixed16 N0f16

@deprecate_binding Fixed32 Q15f16

const UF = (N0f8, N6f10, N4f12, N2f14, N0f16)

@deprecate Fixed(x::Real) convert(Fixed{Int32, 16}, x)

@deprecate ufixed8(x) UFixed8(x)
@deprecate ufixed10(x) UFixed10(x)
@deprecate ufixed12(x) UFixed12(x)
@deprecate ufixed14(x) UFixed14(x)
@deprecate ufixed16(x) UFixed16(x)
@deprecate ufixed8(x) N0f8(x)
@deprecate ufixed10(x) N6f10(x)
@deprecate ufixed12(x) N4f12(x)
@deprecate ufixed14(x) N2f14(x)
@deprecate ufixed16(x) N0f16(x)

Compat.@dep_vectorize_1arg Real ufixed8
Compat.@dep_vectorize_1arg Real ufixed10
Compat.@dep_vectorize_1arg Real ufixed12
Compat.@dep_vectorize_1arg Real ufixed14
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 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(Normed{T,f}, convert(T, n))
end
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

14 changes: 12 additions & 2 deletions src/fixed.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ immutable Fixed{T <: Signed,f} <: FixedPoint{T, f}
Fixed(x) = convert(Fixed{T,f}, x)
end

typealias Fixed16 Fixed{Int32, 16}

reinterpret{T<:Signed, f}(::Type{Fixed{T,f}}, x::T) = Fixed{T,f}(x, 0)

rawtype{T,f}(::Type{Fixed{T,f}}) = T
nbitsfrac{T,f}(::Type{Fixed{T,f}}) = f
floattype{T<:Fixed}(::Type{T}) = floattype(supertype(T))
typechar{X<:Fixed}(::Type{X}) = 'Q'
signbits{X<:Fixed}(::Type{X}) = 1

for T in (Int8, Int16, Int32, Int64)
for f in 0:sizeof(T)*8-1
sym = Symbol(takebuf_string(showtype(_iotypealias, Fixed{T,f})))
@eval begin
typealias $sym Fixed{$T,$f}
export $sym
end
end
end

# basic operators
-{T,f}(x::Fixed{T,f}) = Fixed{T,f}(-x.i,0)
Expand Down
Loading