Skip to content

Commit a00d54c

Browse files
authored
Merge pull request #59 from JuliaMath/teh/approx
Centralize floating-point conversion and implement isapprox
2 parents e3dee04 + 10b396a commit a00d54c

File tree

5 files changed

+78
-29
lines changed

5 files changed

+78
-29
lines changed

src/FixedPointNumbers.jl

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ module FixedPointNumbers
44

55
using Base: reducedim_initarray
66

7-
import Base: ==, <, <=, -, +, *, /, ~,
7+
import Base: ==, <, <=, -, +, *, /, ~, isapprox,
88
convert, promote_rule, show, showcompact, isinteger, abs, decompose,
99
isnan, isinf, isfinite,
1010
zero, one, typemin, typemax, realmin, realmax, eps, sizeof, reinterpret,
11-
trunc, round, floor, ceil, bswap,
11+
float, trunc, round, floor, ceil, bswap,
1212
div, fld, rem, mod, mod1, rem1, fld1, min, max, minmax,
1313
start, next, done, r_promote, reducedim_init
1414

@@ -51,10 +51,24 @@ reinterpret(x::FixedPoint) = x.i
5151
=={T <: FixedPoint}(x::T, y::T) = x.i == y.i
5252
<{T <: FixedPoint}(x::T, y::T) = x.i < y.i
5353
<={T <: FixedPoint}(x::T, y::T) = x.i <= y.i
54+
"""
55+
isapprox(x::FixedPoint, y::FixedPoint; rtol=0, atol=max(eps(x), eps(y)))
56+
57+
For FixedPoint numbers, the default criterion is that `x` and `y` differ by no more than `eps`, the separation between adjacent fixed-point numbers.
58+
"""
59+
function isapprox{T<:FixedPoint}(x::T, y::T; rtol=0, atol=max(eps(x), eps(y)))
60+
maxdiff = T(atol+rtol*max(abs(x), abs(y)))
61+
rx, ry, rd = reinterpret(x), reinterpret(y), reinterpret(maxdiff)
62+
abs(signed(widen1(rx))-signed(widen1(ry))) <= rd
63+
end
64+
function isapprox(x::FixedPoint, y::FixedPoint; rtol=0, atol=max(eps(x), eps(y)))
65+
isapprox(promote(x, y)...; rtol=rtol, atol=atol)
66+
end
5467

5568
# predicates
5669
isinteger{T,f}(x::FixedPoint{T,f}) = (x.i&(1<<f-1)) == 0
5770

71+
# traits
5872
typemax{T<: FixedPoint}(::Type{T}) = T(typemax(rawtype(T)), 0)
5973
typemin{T<: FixedPoint}(::Type{T}) = T(typemin(rawtype(T)), 0)
6074
realmin{T<: FixedPoint}(::Type{T}) = typemin(T)
@@ -70,10 +84,25 @@ widen1(::Type{Int64}) = Int128
7084
widen1(::Type{UInt64}) = UInt128
7185
widen1(x::Integer) = x % widen1(typeof(x))
7286

87+
if VERSION <= v"0.5.0-dev+755"
88+
@generated function floattype{T,f}(::Type{FixedPoint{T,f}})
89+
f>22 ? :(Float64) : :(Float32)
90+
end
91+
else
92+
@pure function floattype{T,f}(::Type{FixedPoint{T,f}})
93+
f>22 ? Float64 : Float32
94+
end
95+
end
96+
floattype(x::FixedPoint) = floattype(supertype(typeof(x)))
97+
98+
7399
include("fixed.jl")
74100
include("ufixed.jl")
75101
include("deprecations.jl")
76102

103+
eps{T<:FixedPoint}(::Type{T}) = T(one(rawtype(T)),0)
104+
eps{T<:FixedPoint}(::T) = eps(T)
105+
sizeof{T<:FixedPoint}(::Type{T}) = sizeof(rawtype(T))
77106

78107
# Promotions for reductions
79108
const Treduce = Float64
@@ -97,6 +126,17 @@ for T in tuple(Fixed16, UF...)
97126
end
98127
end
99128

129+
for f in (:div, :fld, :fld1)
130+
@eval begin
131+
$f{T<:FixedPoint}(x::T, y::T) = $f(reinterpret(x),reinterpret(y))
132+
end
133+
end
134+
for f in (:rem, :mod, :mod1, :rem1, :min, :max)
135+
@eval begin
136+
$f{T<:FixedPoint}(x::T, y::T) = T($f(reinterpret(x),reinterpret(y)),0)
137+
end
138+
end
139+
100140
# When multiplying by a float, reduce two multiplies to one.
101141
# Particularly useful for arrays.
102142
scaledual(Tdual::Type, x) = one(Tdual), x
@@ -115,7 +155,7 @@ function show{T,f}(io::IO, x::FixedPoint{T,f})
115155
print(io, ")")
116156
end
117157
const _log2_10 = 3.321928094887362
118-
showcompact{T,f}(io::IO, x::FixedPoint{T,f}) = show(io, round(convert(Float64,x), ceil(Int,f/_log2_10)))
158+
showcompact{T,f}(io::IO, x::FixedPoint{T,f}) = show(io, round(float(x), ceil(Int,f/_log2_10)))
119159

120160
@noinline function throw_converterror{T<:FixedPoint}(::Type{T}, x)
121161
n = 2^(8*sizeof(T))

src/fixed.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ typealias Fixed16 Fixed{Int32, 16}
1313

1414
rawtype{T,f}(::Type{Fixed{T,f}}) = T
1515
nbitsfrac{T,f}(::Type{Fixed{T,f}}) = f
16+
floattype{T<:Fixed}(::Type{T}) = floattype(supertype(T))
1617

1718
# basic operators
1819
-{T,f}(x::Fixed{T,f}) = Fixed{T,f}(-x.i,0)
@@ -37,6 +38,9 @@ convert{T,f}(::Type{Fixed{T,f}}, x::Rational) = Fixed{T,f}(x.num)/Fixed{T,f}(x.d
3738
rem{T,f}(x::Integer, ::Type{Fixed{T,f}}) = Fixed{T,f}(rem(x,T)<<f,0)
3839
rem{T,f}(x::Real, ::Type{Fixed{T,f}}) = Fixed{T,f}(rem(Integer(trunc(x)),T)<<f + rem(Integer(round(rem(x,1)*(1<<f))),T),0)
3940

41+
# convert{T,f}(::Type{AbstractFloat}, x::Fixed{T,f}) = convert(floattype(x), x)
42+
float(x::Fixed) = convert(floattype(x), x)
43+
4044
convert{T,f}(::Type{BigFloat}, x::Fixed{T,f}) =
4145
convert(BigFloat,x.i>>f) + convert(BigFloat,x.i&(1<<f - 1))/convert(BigFloat,1<<f)
4246
convert{TF<:AbstractFloat,T,f}(::Type{TF}, x::Fixed{T,f}) =

src/ufixed.jl

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ end
1111
rawtype{T,f}(::Type{UFixed{T,f}}) = T
1212
rawtype(x::Number) = rawtype(typeof(x))
1313
nbitsfrac{T,f}(::Type{UFixed{T,f}}) = f
14+
floattype{T<:UFixed}(::Type{T}) = floattype(supertype(T))
1415

1516
typealias UFixed8 UFixed{UInt8,8}
1617
typealias UFixed10 UFixed{UInt16,10}
@@ -65,6 +66,9 @@ rem{T<:UFixed}(x::T, ::Type{T}) = x
6566
rem{T<:UFixed}(x::UFixed, ::Type{T}) = reinterpret(T, _unsafe_trunc(rawtype(T), round((rawone(T)/rawone(x))*reinterpret(x))))
6667
rem{T<:UFixed}(x::Real, ::Type{T}) = reinterpret(T, _unsafe_trunc(rawtype(T), round(rawone(T)*x)))
6768

69+
# convert(::Type{AbstractFloat}, x::UFixed) = convert(floattype(x), x)
70+
float(x::UFixed) = convert(floattype(x), x)
71+
6872
convert(::Type{BigFloat}, x::UFixed) = reinterpret(x)*(1/BigFloat(rawone(x)))
6973
function convert{T<:AbstractFloat}(::Type{T}, x::UFixed)
7074
y = reinterpret(x)*(one(rawtype(x))/convert(T, rawone(x)))
@@ -76,22 +80,8 @@ convert{Ti<:Integer}(::Type{Rational{Ti}}, x::UFixed) = convert(Ti, reinterpret(
7680
convert(::Type{Rational}, x::UFixed) = reinterpret(x)//rawone(x)
7781

7882
# Traits
79-
eps{T<:UFixed}(::Type{T}) = T(one(rawtype(T)),0)
80-
eps{T<:UFixed}(::T) = eps(T)
81-
sizeof{T<:UFixed}(::Type{T}) = sizeof(rawtype(T))
8283
abs(x::UFixed) = x
8384

84-
# Arithmetic
85-
if VERSION <= v"0.5.0-dev+755"
86-
@generated function floattype{T,f}(::Type{UFixed{T,f}})
87-
f>22 ? :(Float64) : :(Float32)
88-
end
89-
else
90-
@pure function floattype{T,f}(::Type{UFixed{T,f}})
91-
f>22 ? Float64 : Float32
92-
end
93-
end
94-
9585
(-){T<:UFixed}(x::T) = T(-reinterpret(x), 0)
9686
(~){T<:UFixed}(x::T) = T(~reinterpret(x), 0)
9787

@@ -133,16 +123,6 @@ isinf(x::UFixed) = false
133123
bswap{f}(x::UFixed{UInt8,f}) = x
134124
bswap(x::UFixed) = typeof(x)(bswap(reinterpret(x)),0)
135125

136-
for f in (:div, :fld, :fld1)
137-
@eval begin
138-
$f{T<:UFixed}(x::T, y::T) = $f(reinterpret(x),reinterpret(y))
139-
end
140-
end
141-
for f in (:rem, :mod, :mod1, :rem1, :min, :max)
142-
@eval begin
143-
$f{T<:UFixed}(x::T, y::T) = T($f(reinterpret(x),reinterpret(y)),0)
144-
end
145-
end
146126
function minmax{T<:UFixed}(x::T, y::T)
147127
a, b = minmax(reinterpret(x), reinterpret(y))
148128
T(a,0), T(b,0)

test/fixed.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ end
7878
@test ( 65.2 % T).i == round(Int, 65.2*512) % Int16
7979
@test (-67.2 % T).i == round(Int, -67.2*512) % Int16
8080

81+
for T in [Fixed{Int8,7}, Fixed{Int16,8}, Fixed{Int16,10}]
82+
testapprox(T) # defined in ufixed.jl
83+
end
84+
8185
# reductions
8286
F8 = Fixed{Int8,8}
8387
a = F8[0.498, 0.1]
@@ -97,6 +101,11 @@ for T in (Float16, Float32, Float64, BigFloat)
97101
@test isa(y, T)
98102
end
99103

104+
# Floating-point conversions
105+
@test isa(float(one(Fixed{Int8,6})), Float32)
106+
@test isa(float(one(Fixed{Int32,18})), Float32)
107+
@test isa(float(one(Fixed{Int32,25})), Float64)
108+
100109
# Show
101110
x = Fixed{Int32,3}(0.25)
102111
iob = IOBuffer()

test/ufixed.jl

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,15 @@ x = UFixed8(0b01010001, 0)
111111
@test ~x == UFixed8(0b10101110, 0)
112112
@test -x == 0xafuf8
113113

114+
@test isa(float(one(UFixed{UInt8,7})), Float32)
115+
@test isa(float(one(UFixed{UInt32,18})), Float32)
116+
@test isa(float(one(UFixed{UInt32,25})), Float64)
117+
114118
for T in (FixedPointNumbers.UF..., UF2...)
115119
x = T(0x10,0)
116120
y = T(0x25,0)
117-
fx = convert(FixedPointNumbers.floattype(T), x)
118-
fy = convert(FixedPointNumbers.floattype(T), y)
121+
fx = float(x)
122+
fy = float(y)
119123
@test y > x
120124
@test y != x
121125
@test typeof(x+y) == T
@@ -168,6 +172,18 @@ for T in (FixedPointNumbers.UF..., UF2...)
168172
testtrunc(eps(T))
169173
end
170174

175+
function testapprox{T}(::Type{T})
176+
for x = typemin(T):eps(T):typemax(T)-eps(T)
177+
y = x+eps(T)
178+
@test x y
179+
@test y x
180+
@test !(x y+eps(T))
181+
end
182+
end
183+
for T in FixedPointNumbers.UF
184+
testapprox(T)
185+
end
186+
171187
@test !(UFixed8(0.5) < UFixed8(0.5))
172188
@test UFixed8(0.5) <= UFixed8(0.5)
173189

0 commit comments

Comments
 (0)