Should conversions between different fixed point representations work (example below) ?
Also I’d like to use the library for some DSP algorithm implementation, so doing things like automatic promotion of multiplies to more bits would be really nice.
Thinking about it, I decided that FixedPointNumbers should not do that, I like the fact that it maintains consistency in the results.
So what I need to do is -
- create a value of 15 bits of precision
- extend to 31 bits
- perform a multiplication
- truncate back to 15 bits
However it looks like the only way to perform that sequence to do intermediate conversions to float and then back.
am I missing something ?
julia> using FixedPointNumbers
julia> t1=Fixed{Int32,15}
FixedPointNumbers.Fixed{Int32,15}
julia> t2=Fixed{Int16,15}
FixedPointNumbers.Fixed{Int16,15}
julia> a=t1(0.25)
0.25Q16f15
julia> b=convert(t2, a)
ERROR: MethodError: Cannot `convert` an object of type FixedPointNumbers.Fixed{Int32,15} to an object of type FixedPointNumbers.Fixed{Int16,15}
This may have arisen from a call to the constructor FixedPointNumbers.Fixed{Int16,15}(...),
since type constructors fall back to convert methods.
julia>
In practice, the Normed
numbers have gotten a lot more practical testing (they are used by the Colors/Images suite), and so we seem to have the following:
julia> using FixedPointNumbers
julia> x = rand(N2f6) # Normed numbers example
1.1N2f6
julia> convert(N6f10, x)
1.0948N6f10
julia> x = rand(Q2f5) # Fixed numbers example
1.66Q2f5
julia> convert(Q6f9, x)
ERROR: MethodError: Cannot `convert` an object of type FixedPointNumbers.Fixed{Int8,5} to an object of type FixedPointNumbers.Fixed{Int16,9}
This may have arisen from a call to the constructor FixedPointNumbers.Fixed{Int16,9}(...),
since type constructors fall back to convert methods.
Any chance you could tweak the implementation for Normed
to build one for Fixed
? And then contribute it?
yes that is something i could do. i’ve only done a PR once, so it will take me a bit to get things figured out again. I’ve already looked at the code and it seems to be straightforward.
However in thinking about this what I really want to be able to do, and this is a generic Julia question is something like
a= Int8(127)
b= Int8(127)
a*b => automatic promotion to Int16
Is there a way through macro-fu or some other method for me to set up a system where I can automatically do that ?
I’m thinking that it simply requires me to generate an operator/method that would capture the types and do so.
function op1(a::Int8, b::Int8) :: Int16
a1 = Int16(a)
b1 = Int16(b)
return a1*b1
end
Should be that easy, right ? well I know it is, I tried it. The problem is that if I try to use the standard multiplication operator, i.e. ‘*’, then I figure it will almost certainly clash with the built in assignment which is to maintain the same type. Interestingly that would not be a problem if the method was actually matched using the return type- which I don’t think is true at this time.
Thanks
One problem with making this the default strategy: Int8^2
→ Int16
, Int16^2
→ Int32
, and Int32^2
→ Int64
. It becomes almost impossible to predict the output type from a computation: go from 2 to 3 multiplies and suddenly the output type changes. Of course sometimes you can compute the same thing in two different ways (e.g., x^4
can be computed in either 2 multiplies or 3), and it would be crazy if the output type depended on how you chose to implement the computation. A lesson is that If you’re going to widen, you need to widen “all the way” immediately.
We used to widen, e.g., UInt8
→ UInt
, and interestingly it was not very popular. I think the lesson is that if you aren’t using one of the “default” types (Int
, UInt
, and Float64
), you probably have a reason and therefore want some manual control. Consequently the dominant behavior (with a few exceptions) is that any widening has to be implemented manually.
2 Likes
I agree. I didn’t mean to imply it should be default behavior . I was just describing what I was trying to do and figuring out a way to make it “look nice”.
I like the fact that the type is precisely conserved and that any modification must be done with an explicit operation.