wl_fixed_to_double() function only correct when fixed value is greater than zero

Jasper St. Pierre jstpierre at mecheye.net
Tue Jan 17 16:53:10 UTC 2023


Hi,

The trick is a variant of the well-known integer-to-float conversion
trick you do if you don't have dedicated hardware for it [0].

64-bit floats have a 52-bit significand and a floating exponent.
Wayland's 24.8 fixed point format can be thought of as a 32-bit
significand and a fixed exponent of 8. To convert from one to the
other. It's enough to stuff the 32-bit significand into the 52-bit
field without it overflowing, so that gets you *a* number, but you
need to set up the correct exponent. If you just try stuffing "8" in
the field, it won't work, because of the implicit "1" at the start of
the significand -- this is what ensures our exponent "buckets" are
non-overlapping, distinct ranges. We need to find the floating point
bucket that has the same precision as our input value. The correct
bucket is actually (num_bits_in_significand - num_bits_in_fixed_point)
-- so that's (52 - 8), which is 44. In the floating point format,
exponents are also biased by 1023, so that explains the "(1023 + 44)
<< 52". This is placing the right exponent in the exponent field of
the float.

The extra bit at (1 << 51) is a bias to handle signed values correctly
-- this way, adding a signed value to the float won't take us out of
our bucket.

However, because of that implicit 1, our floats are offset to a
different range. That's easy enough, we just need to subtract out the
start of that range. If you interpret ((1023 + 44) << 52) + (1 << 51)
as a double float, you see it ends up with a value of 0x180000000000,
which is indeed (3 << 43). There's probably a more direct way to
compute that, but I don't know what it is.

No clue if this helped at all, or just made things more confusing. In
any case, the existing code is correct, but a direct port to another
language might be tricky. If you want a portable method, I'd recommend
what Pekka suggests, which is just (v / 256.0f).

[0] https://cr.yp.to/2005-590/powerpc-cwg.pdf#page=104 , "Convert
32-Bit Unsigned Integer to Floating-Point Code Sequence"

On Tue, Jan 17, 2023 at 7:27 AM Pekka Paalanen <ppaalanen at gmail.com> wrote:
>
> On Mon, 16 Jan 2023 14:43:43 +0800
> "程安絮" <chenganxu2014 at sina.com> wrote:
>
> > In file main.go is my implementation of FixedToFloat64 vs
> > wl_fixed_to_double, the comments can help you to understand my codes.
> > Since event wl_touch::orientation's orientation arg is fixed value
> > and can be negative, I think this bug is not acceptable. Besides, I
> > don't understand how wl_fixed_to_double works, can anyone illustrate
> > that for me?
> >
> > --------------------------------程安絮
>
> Hi,
>
> that wl_fixed_to_double() implementation in C is a far too smart trick
> for no other good reason than trying to be fast in a case where speed
> is not needed.
>
> The human readable conversion would be:
>
> double wl_fixed_to_double(wl_fixed_t f)
> {
>         return (double)f / 256.0;
> }
>
> The trick code seems to be just fine through. I've attached a test
> program that iterates through all possible wl_fixed_t values and
> ensures the trivial conversion agrees with the trick conversion.
>
> I cannot explain how it works, but it does seem to work.
>
>
> Thanks,
> pq



-- 
  Jasper


More information about the wayland-devel mailing list