Questions about fixed point numbers

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^2Int16, Int16^2Int32, and Int32^2Int64. 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., UInt8UInt, 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.