I am using FixedPointNumbers.jl to implement an approximation algorithm, and I have been running into the following issue:
julia> using FixedPointNumbers
julia> FP = Fixed{Int16, 4}
Q11f4 (alias for Fixed{Int16, 4})
julia> typemax(FP)
2047.94Q11f4
julia> 2099 > typemax(FP)
ERROR: ArgumentError: Q11f4 is a 16-bit type representing 65536 values from -2048.0 to 2047.94; cannot represent 2099
Would you consider this a bug, or is leaving comparisons undefined when number falls out of the type range the standard practice?
If throwing an error as above is the standard practice to prevent overflow issues, what should I do instead? Calling 2099 > Float64(typemax(FP)) feels like overkill.
FWIW, Julia’s builtin types do not have this behavior: 130 > typemax(Int8) is true.
Looks like there is no specialized method for comparison, so a generic method is attempting to promote the Int to the fixed-point number, and failing since the Int is too large.
An easy fix would be to add a method which first checks if the non fixed-point number is greater/less than the typemax/min of the fixed-point number before trying the promote and compare fallback. Something along the lines of:
function Base.:<(x::T, y::U) where {T<:Real, U<:FixedPoint}
if x > round(T, typemax(y), RoundUp)
return false
elseif x < round(T, typemin(y), RoundDown)
return true
end
_x, _y = promote(x,y)
return <(_x.i, _y.i)
end
Base.:<(x::U, y::T) where {U<:Fixed, T<:Real} = !(y < x)
Good catch! My suggestion doesn’t change promotion or promotion rules, just when promotion happens for this specific case. Looks like the out-of-range comparisons should be ≤ or ≥, since, e.g. for an Int, 2047.1 rounded up to 2048 is also (obviously) too big.
function Base.:<(x::T, y::U) where {T<:Real, U<:FixedPoint}
if x >= round(T, typemax(y), RoundUp)
return false
elseif x <= round(T, typemin(y), RoundDown)
return true
end
_x, _y = promote(x,y)
return <(_x.i, _y.i)
end