milankl/SoftPosit.jl

Support for QuadGK

Closed this issue · 11 comments

It'd cool to be able to use this package with QuadGK, which enables numerical integration with custom Julia types.

However, this currently doesn't work because the numeric type must be an AbstractFloat or a type that can be converted to an AbstractFloat. This initial issue would be solved by #14.

After that, the first issue I've faced is that there is no eps method for posit numbers:

julia> quadgk(x -> x ^ 2, Posit16(0), Posit16(1))
ERROR: MethodError: no method matching eps(::Type{Posit16})

Okay, I think this is already reason enough to make AbstractPosit <: AbstractFloat, i'll change that. Fingers crossed, that we don't come across another good reason to change it back. From your error message it seems that there is only the eps(::Type) functionality needed and not the eps(::AbstractFloat) which seems to be based on nextfloat and prevfloat. I'm happy to implement that, Posits number are also sorted by bits, that means a bit-wise plus one should do the job for nextfloat(::Posit), and vice versa for prevfloat(::Posit).

I think you need both eps(::Type) and eps(::Posit), because eps for an instance gives the precision around that number, while eps(T) gives the precision around 1

Good, now that there is eps we get another error:

julia> quadgk(x -> x ^ 2, Posit16(0), Posit16(1))
ERROR: MethodError: no method matching exponent(::Posit16)

Any idea whether the exponent function is directly needed or only inside the eps(::Posit) function? Still wondering whether the best way to extract the regime and exponent bits and calculate the base 2 exponent with integer arithmetics or to implement a nextfloat, prevfloat variant for Posits (which should be doable by reinterpreting it as UInt and adding 1) and then to calculate eps based on these.

I think it's only inside eps(::Posit), this is the function that is being called:

eps(x::AbstractFloat) = isfinite(x) ? abs(x) >= floatmin(x) ? ldexp(eps(typeof(x)), exponent(x)) : nextfloat(zero(x)) : oftype(x, NaN)

isfinite, nextfloat, prevfloat, sign, signbit are now implemented. However, following the underlying idea of posit arithmetic (look at the posit circle), we should have a wrap-around behaviour for posits similar to integers

julia> Float64(nextfloat(nextfloat(Posit8(64))))
-64.0

(64 is floatmax(Posit8)). Do you think this causes any issues? Alternatively, we could also aim for a saturation behaviour such that notareal(Posit8) == nextfloat(notareal(Posit8)).

Do you think this causes any issues?

No idea at all! But this could be a good occasion to check if it makes sense or not.

Anyway, a comprehensive set of tests is really needed to make sure that everything is behaving as expected at any time.

I plan to implement the eps(::AbstractPosit) function simply as

eps(p::AbstractPosit) == max(p-prevfloat(p),nextfloat(p)-p)

which avoids the exponent function that you mentioned earlier raises and error as non-existent. Yes, we could also implement the exponent function directly, however, for posits that means we would need to count the regime bits, which is certainly done in the C library somewhere, but I think that here is an easier way for the beginning.

That might work as an interim solution!

Btw, this works now

julia> quadgk(x -> x ^ 2, Posit16(0), Posit16(1))
(Posit16(0x255b), Posit16(0x0080))
julia> Float64.(ans)
(0.33367919921875, 6.103515625e-5)

This is very nice! Comparison with Float16:

julia> using SoftPosit, QuadGK

julia> Float64.(quadgk(x -> x ^ 2, Posit16(0), Posit16(1)))
(0.33367919921875, 6.103515625e-5)

julia> Float64.(quadgk(x -> x ^ 2, Float16(0), Float16(1)))
(0.3330078125, 0.00048828125)