[cairo] Patch: speeding up _cairo_fixed_from_double

Bill Spitzak spitzak at d2.com
Wed Oct 5 11:50:51 PDT 2005


I also posted the same thing some time ago, not sure what happened to 
that as I never got any response. My code uses the exact same trick (and 
I'm sure shares the same original source). The only difference is that I 
also made a fast test to see if the conversion is out of range that may 
be useful. In our software using similar code to replace float->int the 
speedup is considerable, like the 25% Tim Rowley claims.

I would describe the trick as "add a large enough constant so that all 
the interesting range has the same exponent, then mask off the bits set 
by this constant"

Note: this comment is misleading, the code does the rounding that the 
FPU is set to, which is round-to-nearest by default, not floor:
/* 236 * 1.5,  (52-_shiftamt=36) uses limited precision to floor */

=== cut here for fast_DoubleToFixed_test.c ===
/*
Copyright (c) 2005 by Bill Spitzak
Permission is hereby granted to use this code in any manner.
*/

#include <stdio.h>
#include <stdlib.h>

#if __BIG_ENDIAN_
#define iman_ 1
#else
#define iman_ 0
#endif

/**
   Fast version of XDoubleToFixed(f)
   Works for -32768.0 .. 32767.99999
   Requires IEEE floating point.
*/
inline int fast_DoubleToFixed(double val) {
   val = val + (68719476736.0*1.5);
   return ((long*)&val)[iman_];
}

/**
   Puts fast_DoubleToFixed() into *out.
   Returns true if out of range error.
   Requires IEEE floating point.
*/
inline int checkDoubleToFixed(double val, int* out) {
   val = val + (32768+68719476736.0*1.5);
   *out = ((int*)&val)[iman_]^0x80000000;
   return ((int*)&val)[1-iman_] != 0x42380000;
}

#define XFixedToDouble(f)    (((double) (f)) / 65536)

int main(int argc, char** argv) {
   int i, j;
   double f;
   if (argc < 2) {
     fprintf(stderr, "Return fast_rint(x) for each argument\n");
     return 1;
   }
   for (i = 1; i < argc; i++) {
     f = strtod(argv[i],0);
     j = fast_DoubleToFixed(f);
     printf("fast_DoubleToFixed(%g) == %.10g (0x%08x)\n", f, 
XFixedToDouble(j), j);
     if (checkDoubleToFixed(f, &j)) {
       printf("checkDoubleToFixed(%g) == FAIL\n", f);
     } else {
       printf("checkDoubleToFixed(%g) == %.10g (0x%08x)\n", f, 
XFixedToDouble(j), j);
     }
   }
   return 0;
}
=== cut here ===

Tim Rowley wrote:
> Doing some profiling runs with gradients, I noticed that a lot of the 
> time was disappearing into _cairo_fixed_from_double (and its 
> descendents, ftol and floor).  Searching around I found this page that 
> talks about doing fast floating point to integer conversion:
> 
>     http://www.stereopsis.com/FPU.html
> 
> Adapting one of the methods for cairo gave about a 25% speed increase in 
> our gradient tests.


More information about the cairo mailing list