Re: Fixed point cairo.. or no cairo?



Hi,

Thanks a lot for the pointers! I have added comments below.

On Mon, 2006-08-14 at 09:34 -0700, Carl Worth wrote:
> On Mon, 14 Aug 2006 10:09:48 +0300, Jorn Baayen wrote:
> >
> > On Tue, 2006-08-08 at 10:46 -0700, Carl Worth wrote:
> >
> > > I'm planning to put new fixed<->floating-point conversion code into
> > > cairo this week.
> >
> > Any progress on this?
> 
> Nope. Didn't happen yet.
> 
> If someone wants to look at it, here are some details of what could be
> done:
> 
> 1) The current implementation has complete function-call
>    overhead. Could easily be reduced with inlining, (if the compiler's
>    not doing that automatically), or macros.
> 
> So that's the easy one that doesn't actually change the code at
> all. Next is the stuff that will take a little more investigation:
> 
> 2) There's some discussion of the correctness of the rounding of the
>    current code here. For any new implementation, I would like to see
>    comments indicating that the code respects the concerns raised
>    here:
> 
> 	https://bugs.freedesktop.org/show_bug.cgi?id=4846
> 
>    (And better, than comments would be some test cases that ensure the
>    desired behavior is met.)
> 
> 3) Finally, a couple of people have proposed much faster mechanisms
>    for implementing the conversion. First was Bill Spitzak who
>    provided some code here:
> 
> 	http://lists.freedesktop.org/archives/cairo/2005-June/004419.html
> 
>    Then later, Tim Rowley independently provided a source explaining
>    the code along with a very similar implementation:
> 
> 	http://thread.gmane.org/gmane.comp.lib.cairo/5228/focus=5228
> 
>    What I've been planning to do for a while was to write some
>    configure-time check to ensure that the assumptions made in this
>    code (IEEE floating-point) are valid and conditionally compile this
>    code only where we know it will work.

I had a look at the various options available and their rounding
correctness. What I was able to find out was:

o The current cairo_fixed_from_double() does not adhere to the rules of
  either mathematical nor banker's rounding.

o Correct mathematical rounding can be achieved using round(), this
  however is even slower than the current cairo_fixed_from_double()
  implementation. Also round() is c99.

o Correct and fast banker's rounding can be achieved using Sree's
  Real2Int[1]. This depends on doubles being in IEEE format- do
  any of the platforms cairo does/will run on *not* use IEEE
  doubles?

o I was not able to come up with an implementation doing fast and
  correct mathematical rounding. This is probably just me, though.


I am attaching code illustrating all these cases.


Thanks,

Jorn



[1] http://www.stereopsis.com/FPU.html


-- 
OpenedHand Ltd.
http://o-hand.com/
/* gcc test.c -o test -lm -std=c99 `pkg-config --libs --cflags glib-2.0` */

#include <math.h>
#include <glib.h>

/* #define SHOWNUMBERS 1 */

#define N_TESTS 21

const struct {
        double d;
        gint32 i;
} tests[N_TESTS] = {
        { + (.5 - 0x1p-54), 0 },
        { - (.5 - 0x1p-54), 0 },
        { 1.5, 2 },
        { 2.5001, 3 },
        { 2.6, 3 },
        { 2.9, 3 },
        { 1.0, 1 },
        { 0.9, 1 },
        { 0.5, 1 },
        { -0.5, -1 },
        { 0.49, 0 },
        { 0.51, 1 },
        { 8.5, 9 },
        { 9.5, 10 },
        { 8.75, 9 },
        { -0.49, 0 },
        { -0.51, -1 },
        { -1.5, -2 },
        { -1.2, -1 },
        { -3.2, -3 },
        { -3.5, -4 }
};

#ifdef WORDS_BIGENDIAN

  #define iman 1

#else

  #define iman 0

#endif

const double _double2fixmagic52 = 6755399441055744.0; /* 2 ^ 52 * 1.5        */
const double _double2fixmagic36 = 103079215104.0;     /* 2 ^ (52 - 16) * 1.5 */

/* This is the fastest, but it uses banker's rounding. */
gint32
fixed_from_double_sree (double val)
{
        val = val + _double2fixmagic52;
        
        return ((gint32 *) &val)[iman];
}

/* This tries to do mathematical rounding right, but fails. */
gint32
fixed_from_double_useless (double val)
{
        val = val + 0.5 + _double2fixmagic52;
        
        return ((gint32 *) &val)[iman];
}

/* This tries to do mathematical rounding right, but fails. */
gint32
fixed_from_double_shift (double val)
{
        val = val + 0.5 + _double2fixmagic36;

	return ((gint32 *) &val)[iman] >> 16;
}

/* This is the only one with 100% correct mathematical rounding,
 * but it is also the slowest. */
gint32
fixed_from_double_round (double val)
{
        val = round (val) + _double2fixmagic52;

	return ((gint32 *) &val)[iman];
}

/* This is what cairo currently uses. It's slow and does not stick to a
 * particular style of rounding. */
gint32
fixed_from_double_cairo (double val)
{
        return (gint32) floor (val + 0.5);
}

static void
run (const char *desc, gint32 (* func) (double val))
{
        gint32 result;
        GTimer *timer;
        int i, passed = 0;

        g_print ("=== %s ===\n", desc);

        timer = g_timer_new ();
        g_timer_start (timer);
        
        for (i = 0; i < N_TESTS; i++) {
                result = func (tests[i].d);

                if (result == tests[i].i) {
#ifdef SHOWNUMBERS
                        g_print ("Passed test #%d: d: %.20lf i: %d result: %d.\n",
                                i, tests[i].d, tests[i].i, result);
#endif

                        passed++;
                } else {
#ifdef SHOWNUMBERS
                        g_print ("Failed test #%d: d: %.20lf i: %d result: %d.\n",
                                i, tests[i].d, tests[i].i, result);
#endif
                }
        }

        g_print ("\nElapsed: %lfs\n", g_timer_elapsed (timer, NULL));
        g_timer_destroy (timer);
        
        g_print ("Passed: %f\%\n\n\n",
                 ((double) passed / (double) N_TESTS) * 100.0);
}

int
main (int argc, char **argv)
{
        run ("Failed attempt at fast mathematical rounding #1",
             fixed_from_double_useless);
        run ("Failed attempt at fast mathematical rounding #2",
             fixed_from_double_shift);
        run ("Sree's Real2Int (banker's rounding)", fixed_from_double_sree);
        run ("Correct, but slow mathematical rounding using round()",
             fixed_from_double_round);
        run ("Current cairo function", fixed_from_double_cairo);
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]