From 213e30d8fcd33dfc9adf61869a623b8631767fd5 Mon Sep 17 00:00:00 2001 From: kimikage Date: Thu, 13 Aug 2020 21:08:24 +0900 Subject: [PATCH 1/3] Improve `rem` for `Fixed` --- src/fixed.jl | 18 ++++++++++++++---- test/fixed.jl | 10 +++++++--- test/normed.jl | 4 ++-- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/fixed.jl b/src/fixed.jl index e2082814..894d010a 100644 --- a/src/fixed.jl +++ b/src/fixed.jl @@ -104,10 +104,20 @@ end /(x::Fixed{T,f}, y::Fixed{T,f}) where {T,f} = Fixed{T,f}(div(convert(widen(T), x.i) << f, y.i), 0) - -rem(x::Integer, ::Type{Fixed{T,f}}) where {T,f} = Fixed{T,f}(rem(x,T)< 1.0f0 % Normed{UInt32,f} == oneunit(Normed{UInt32,f}), 1:32) From bd6e10ca5835cba9a4ee48980c422cfc4deeb9e7 Mon Sep 17 00:00:00 2001 From: kimikage Date: Wed, 19 Aug 2020 14:18:53 +0900 Subject: [PATCH 2/3] Specialize multiplication for `Fixed` --- src/fixed.jl | 55 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/src/fixed.jl b/src/fixed.jl index 894d010a..5e3fe3a3 100644 --- a/src/fixed.jl +++ b/src/fixed.jl @@ -95,15 +95,6 @@ function _convert(::Type{F}, x::Rational) where {T, f, F <: Fixed{T,f}} end end -# unchecked arithmetic - -# with truncation: -#*(x::Fixed{T,f}, y::Fixed{T,f}) = Fixed{T,f}(Base.widemul(x.i,y.i)>>f,0) -# with rounding up: -*(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) - -/(x::Fixed{T,f}, y::Fixed{T,f}) where {T,f} = Fixed{T,f}(div(convert(widen(T), x.i) << f, y.i), 0) - rem(x::F, ::Type{F}) where {F <: Fixed} = x function rem(x::Fixed, ::Type{F}) where {T, f, F <: Fixed{T,f}} f2 = nbitsfrac(typeof(x)) @@ -128,6 +119,52 @@ function Base.Rational(x::Fixed{T,f}) where {T, f} f < bitwidth(T)-1 ? x.i//rawone(x) : x.i//(one(widen1(T))<>> (bitwidth(T) - f - 1)) + half = oneunit(T) << (f - 1) + c = half - (xf === half) + (x + c) >> f +end +div_2f(x::T, ::Val{0}) where {T} = x + +# wrapping arithmetic +function wrapping_mul(x::F, y::F) where {T <: Union{Int8,Int16,Int32,Int64}, f, F <: Fixed{T,f}} + z = widemul(x.i, y.i) + F(div_2f(z, Val(Int(f))) % T, 0) +end + +# saturating arithmetic +function saturating_mul(x::F, y::F) where {T <: Union{Int8,Int16,Int32,Int64}, f, F <: Fixed{T,f}} + if f >= bitwidth(T) - 1 + z = min(widemul(x.i, y.i), widen1(typemax(T)) << f) + else + z = clamp(widemul(x.i, y.i), widen1(typemin(T)) << f, widen1(typemax(T)) << f) + end + F(div_2f(z, Val(Int(f))) % T, 0) +end + +# checked arithmetic +function checked_mul(x::F, y::F) where {T <: Union{Int8,Int16,Int32,Int64}, f, F <: Fixed{T,f}} + z = widemul(x.i, y.i) + if f < 1 + m = widen1(typemax(T)) + 0x1 + n = widen1(typemin(T)) + else + half = widen1(oneunit(T)) << (f - 1) + m = widen1(typemax(T)) << f + half + n = widen1(typemin(T)) << f - half + end + (n <= z) & (z < m) || throw_overflowerror(:*, x, y) + F(div_2f(z, Val(Int(f))) % T, 0) +end + +# with truncation: +#*(x::Fixed{T,f}, y::Fixed{T,f}) = Fixed{T,f}(Base.widemul(x.i,y.i)>>f,0) +# with rounding up: +#*(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) + +/(x::Fixed{T,f}, y::Fixed{T,f}) where {T,f} = Fixed{T,f}(div(convert(widen(T), x.i) << f, y.i), 0) + function trunc(x::Fixed{T,f}) where {T, f} f == 0 && return x f == bitwidth(T) && return zero(x) # TODO: remove this line From 9e7c522f43c5bbb5fa11a8b14c76e751e81eec64 Mon Sep 17 00:00:00 2001 From: kimikage Date: Fri, 21 Aug 2020 20:04:42 +0900 Subject: [PATCH 3/3] Add `mul_with_rounding` for `Fixed` This provides the multiplication compatible with v0.8 and earlier as `mul_with_rounding(x, y, RoundNearestTiesUp)`. This also provides the rounding-down version `mul_with_rounding(x, y, RoundDown)`. The function is informative and not exported. --- src/fixed.jl | 16 ++++++++++++---- test/fixed.jl | 7 +++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/fixed.jl b/src/fixed.jl index 5e3fe3a3..e30b7c99 100644 --- a/src/fixed.jl +++ b/src/fixed.jl @@ -158,10 +158,18 @@ function checked_mul(x::F, y::F) where {T <: Union{Int8,Int16,Int32,Int64}, f, F F(div_2f(z, Val(Int(f))) % T, 0) end -# with truncation: -#*(x::Fixed{T,f}, y::Fixed{T,f}) = Fixed{T,f}(Base.widemul(x.i,y.i)>>f,0) -# with rounding up: -#*(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) +function mul_with_rounding(x::F, y::F, ::RoundingMode{:Nearest}) where {F <: Fixed} + wrapping_mul(x, y) +end +function mul_with_rounding(x::F, y::F, ::RoundingMode{:NearestTiesUp}) where + {T <: Union{Int8,Int16,Int32,Int64}, f, F <: Fixed{T, f}} + z = widemul(x.i, y.i) + F(((z + (oftype(z, 1) << f >>> 1)) >> f) % T, 0) +end +function mul_with_rounding(x::F, y::F, ::RoundingMode{:Down}) where + {T <: Union{Int8,Int16,Int32,Int64}, f, F <: Fixed{T, f}} + F((widemul(x.i, y.i) >> f) % T, 0) +end /(x::Fixed{T,f}, y::Fixed{T,f}) where {T,f} = Fixed{T,f}(div(convert(widen(T), x.i) << f, y.i), 0) diff --git a/test/fixed.jl b/test/fixed.jl index c1275613..80b805be 100644 --- a/test/fixed.jl +++ b/test/fixed.jl @@ -409,6 +409,13 @@ end @test all(((x, y),) -> !(typemin(F) <= fmul(x, y) <= typemax(F)) || wrapping_mul(x, y) === checked_mul(x, y), xys) end + + FixedPointNumbers.mul_with_rounding(1.5Q6f1, 0.5Q6f1, RoundNearest) === 1.0Q6f1 + FixedPointNumbers.mul_with_rounding(1.5Q6f1, -0.5Q6f1, RoundNearest) === -1.0Q6f1 + FixedPointNumbers.mul_with_rounding(1.5Q6f1, 0.5Q6f1, RoundNearestTiesUp) === 1.0Q6f1 + FixedPointNumbers.mul_with_rounding(1.5Q6f1, -0.5Q6f1, RoundNearestTiesUp) === -0.5Q6f1 + FixedPointNumbers.mul_with_rounding(1.5Q6f1, 0.5Q6f1, RoundDown) === 0.5Q6f1 + FixedPointNumbers.mul_with_rounding(1.5Q6f1, -0.5Q6f1, RoundDown) === -1.0Q6f1 end @testset "rounding" begin