Skip to content

Commit cf9ebc4

Browse files
ssfrrtimholy
authored andcommitted
avoids integer overflow on left shift (#106)
in several places the conversion machinery uses `1<<f`, which is a problem for `Fixed{Int64, 63}`, and `Fixed{Int32, 31}` on 32-bit machines. This changes those to `one(widen1(T))<<f`. Fixes #104.
1 parent 861b036 commit cf9ebc4

File tree

2 files changed

+19
-6
lines changed

2 files changed

+19
-6
lines changed

src/fixed.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,26 @@ abs(x::Fixed{T,f}) where {T,f} = Fixed{T,f}(abs(x.i),0)
3737
# with truncation:
3838
#*{f}(x::Fixed32{f}, y::Fixed32{f}) = Fixed32{f}(Base.widemul(x.i,y.i)>>f,0)
3939
# with rounding up:
40-
*(x::Fixed{T,f}, y::Fixed{T,f}) where {T,f} = Fixed{T,f}((Base.widemul(x.i,y.i) + (convert(widen(T), 1) << (f-1) ))>>f,0)
40+
*(x::Fixed{T,f}, y::Fixed{T,f}) where {T,f} = Fixed{T,f}((Base.widemul(x.i,y.i) + (one(widen(T)) << (f-1)))>>f,0)
4141

4242
/(x::Fixed{T,f}, y::Fixed{T,f}) where {T,f} = Fixed{T,f}(div(convert(widen(T), x.i) << f, y.i), 0)
4343

4444

4545
# # conversions and promotions
4646
convert(::Type{Fixed{T,f}}, x::Integer) where {T,f} = Fixed{T,f}(round(T, convert(widen1(T),x)<<f),0)
47-
convert(::Type{Fixed{T,f}}, x::AbstractFloat) where {T,f} = Fixed{T,f}(round(T, trunc(widen1(T),x)<<f + rem(x,1)*(1<<f)),0)
47+
convert(::Type{Fixed{T,f}}, x::AbstractFloat) where {T,f} = Fixed{T,f}(round(T, trunc(widen1(T),x)<<f + rem(x,1)*(one(widen1(T))<<f)),0)
4848
convert(::Type{Fixed{T,f}}, x::Rational) where {T,f} = Fixed{T,f}(x.num)/Fixed{T,f}(x.den)
4949

5050
rem(x::Integer, ::Type{Fixed{T,f}}) where {T,f} = Fixed{T,f}(rem(x,T)<<f,0)
51-
rem(x::Real, ::Type{Fixed{T,f}}) where {T,f} = Fixed{T,f}(rem(Integer(trunc(x)),T)<<f + rem(Integer(round(rem(x,1)*(1<<f))),T),0)
51+
rem(x::Real, ::Type{Fixed{T,f}}) where {T,f} = Fixed{T,f}(rem(Integer(trunc(x)),T)<<f + rem(Integer(round(rem(x,1)*(one(widen1(T))<<f))),T),0)
5252

5353
# convert{T,f}(::Type{AbstractFloat}, x::Fixed{T,f}) = convert(floattype(x), x)
5454
float(x::Fixed) = convert(floattype(x), x)
5555

5656
convert(::Type{BigFloat}, x::Fixed{T,f}) where {T,f} =
57-
convert(BigFloat,x.i>>f) + convert(BigFloat,x.i&(1<<f - 1))/convert(BigFloat,1<<f)
57+
convert(BigFloat,x.i>>f) + convert(BigFloat,x.i&(one(widen1(T))<<f - 1))/convert(BigFloat,one(widen1(T))<<f)
5858
convert(::Type{TF}, x::Fixed{T,f}) where {TF <: AbstractFloat,T,f} =
59-
convert(TF,x.i>>f) + convert(TF,x.i&(1<<f - 1))/convert(TF,1<<f)
59+
convert(TF,x.i>>f) + convert(TF,x.i&(one(widen1(T))<<f - 1))/convert(TF,one(widen1(T))<<f)
6060

6161
convert(::Type{Bool}, x::Fixed{T,f}) where {T,f} = x.i!=0
6262
function convert(::Type{Integer}, x::Fixed{T,f}) where {T,f}
@@ -69,7 +69,7 @@ function convert(::Type{TI}, x::Fixed{T,f}) where {TI <: Integer,T,f}
6969
end
7070

7171
convert(::Type{TR}, x::Fixed{T,f}) where {TR <: Rational,T,f} =
72-
convert(TR, x.i>>f + (x.i&(1<<f-1))//(1<<f))
72+
convert(TR, x.i>>f + (x.i&(1<<f-1))//(one(widen1(T))<<f))
7373

7474
promote_rule(ft::Type{Fixed{T,f}}, ::Type{TI}) where {T,f,TI <: Integer} = Fixed{T,f}
7575
promote_rule(::Type{Fixed{T,f}}, ::Type{TF}) where {T,f,TF <: AbstractFloat} = TF

test/fixed.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,16 @@ end
135135
@test_throws InexactError Fixed{Int32,16}(complex(1.0, 1.0))
136136
@test Fixed{Int32,16}(complex(1.0, 0.0)) == 1
137137
@test Fixed{Int32,16}(Base.TwicePrecision(1.0, 0.0)) == 1
138+
139+
# test all-fractional fixed-point numbers (issue #104)
140+
for (T, f) in ((Int8, 7),
141+
(Int16, 15),
142+
(Int32, 31),
143+
(Int64, 63))
144+
tmax = typemax(Fixed{T, f})
145+
@test tmax == BigInt(typemax(T)) / BigInt(2)^f
146+
tol = (tmax + BigFloat(1.0)) / (sizeof(T) * 8)
147+
for x in linspace(-1, BigFloat(tmax)-tol, 50)
148+
@test abs(Fixed{T, f}(x) - x) <= tol
149+
end
150+
end

0 commit comments

Comments
 (0)