[RFC] What should the arithmetic within the FixedPointNumbers be

Couldn’t load take an optional argument indicating the desired return type?

For most other things in Julia, if you have a big library of code and you want to change the behavior, you pass in a different type. i.e. if I want to test the roundoff sensitivity of my floating-point algorithm, I might pass in some other precision like BigFloat and see how the numbers change, and I focus on writing my library in a type-generic way — you don’t have a global setting for the floating-point type. Similarly, if I want to check for integer overflow I might pass in a SafeInt. Why should fixed-point arithmetic be different?

4 Likes

OK, we won’t use Preferences. I’m traveling so can’t check things most of the day, but I have my laptop now…I was remembering we had a --checked mode on julia itself, and was wondering why people were reacting to this so differently, but it doesn’t seem to be true. Maybe I was remembering very old Julia versions.

1 Like

Some examples here boil my blood…
To me it seems there is some confusion how fixed-point artihmetics work in general / why people are bothered with fixed-point at all.

example:

N0f8 + N0f8 → UQ1f8, we need one bit for b* 2^(0) = b*1
Why should addition add another fractional bit which represents a resolution which was not in the operands? For normalized fixed points using wrap-around addition as currently implemented is also bad default without any mathematical meaning deduced from the operands property 0.0 <= x <= 1.0.

other example:

Assume we want to maintain highest precision. The needed type / size of acc
than depends on our calculation order. (Fixed Points and Floating Points are both not associative, see gcc’s -funsafe-math-optimizations for floats, FloatingPointMath - GCC Wiki) ) For a fully serial addition chain we need 1 extra bit per addition (Nel -1), so we can keep the potential overflow bit. A parallel tree-like scheme will only need one additional bit per stage, so the result will have log2(Nel) extra bits.
Everything here depends on the input list!

What would be a usecase for this? This high-level generality will never be used in the wild by people concerned with stable and performant fixed-point implementations. Actually I consider this example in productive code extremely dangerous!
(half-productive is ok: you are prototyping to see what your maximum accumulator size should be for some realistic inputs)

Agree that Fixed-Point-Numbers should behave as any other Julia number, but you have to keep in mind that they are not associative and have limitations on possible values and value range. They are no plug-in replacement for floats.

mean(img) where img::RGB{N0f8}. It’s used in the wild a lot. And no, we don’t initialize the accumulator this way, we use one with much higher precision. But you need to take care to do that. The example was just used to illustrate the perils of automatic widening.

1 Like

see where this goes… I’m also not a fan of widening behavior by default.

As problematic as this loop is some code which uses splat ... and some form of reduction.
e.g.

sum(x...) = reduce(+, x...)
mean(x...)
norm(x...)
normalize(x...)
dot(x, y) # maybe critical

Possibly a few more. They are used a lot.
Is there any problem in overloading these?
The number of arguments is known when called and the accumulator can be chosen to achieve maximum accuracy. The return value is converted to the input type.
This matches mean(img) as far as I understood.

And for everything else say clearly: “please implement the rest for yourself and think of limited range / accuracy”.