]> icculus.org git repositories - btb/d2x.git/blob - maths/fixc.c
change u_int*_t to C99 standard uint*_t
[btb/d2x.git] / maths / fixc.c
1 /* $Id: fixc.c,v 1.7 2004-08-28 23:17:45 schaffner Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 /*
16  *
17  * C version of fixed point library
18  *
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <conf.h>
23 #endif
24
25 #ifdef RCS
26 static char rcsid[] = "$Id: fixc.c,v 1.7 2004-08-28 23:17:45 schaffner Exp $";
27 #endif
28
29 #include <stdlib.h>
30 #include <math.h>
31
32 #include "error.h"
33 #include "maths.h"
34
35 #ifdef NO_FIX_INLINE
36 #ifdef _MSC_VER
37 #pragma message ("warning: FIX NOT INLINED")
38 #else
39 // #warning "FIX NOT INLINED"        fixc is now stable
40 #endif
41 #endif
42
43 extern ubyte guess_table[];
44 extern short sincos_table[];
45 extern ushort asin_table[];
46 extern ushort acos_table[];
47 extern fix isqrt_guess_table[];
48
49 //negate a quad
50 void fixquadnegate(quadint *q)
51 {
52         q->low  = 0 - q->low;
53         q->high = 0 - q->high - (q->low != 0);
54 }
55
56 //multiply two ints & add 64-bit result to 64-bit sum
57 void fixmulaccum(quadint *q,fix a,fix b)
58 {
59         uint32_t aa, bb;
60         uint32_t ah, al, bh, bl;
61         uint32_t t, c = 0, old;
62         int neg;
63
64         neg = ((a^b) < 0);
65
66         aa = labs(a); bb = labs(b);
67
68         ah = aa>>16;  al = aa&0xffff;
69         bh = bb>>16;  bl = bb&0xffff;
70
71         t = ah*bl + bh*al;
72
73         if (neg)
74                 fixquadnegate(q);
75
76         old = q->low;
77         q->low += al*bl;
78         if (q->low < old) q->high++;
79         
80         old = q->low;
81         q->low += (t<<16);
82         if (q->low < old) q->high++;
83         
84         q->high += ah*bh + (t>>16) + c;
85         
86         if (neg)
87                 fixquadnegate(q);
88
89 }
90
91 //extract a fix from a quad product
92 fix fixquadadjust(quadint *q)
93 {
94         return (q->high<<16) + (q->low>>16);
95 }
96
97
98 #define EPSILON (F1_0/100)
99
100 #ifdef _MSC_VER
101 #define QLONG __int64
102 #else
103 #define QLONG long long
104 #endif
105
106 #ifdef NO_FIX_INLINE
107 fix fixmul(fix a, fix b) {
108 /*        return (fix)(((double)a*(double)b)/65536.0);*/
109 /*        register fix ret;
110         asm("imul %%edx; shrd $16,%%edx,%%eax" : "=a" (ret) : "a" (a), "d" (b) : "%edx");
111         return ret;                                 */
112         return (fix)((((QLONG)a)*b) >> 16);
113 }
114
115 fix fixdiv(fix a, fix b)
116 {
117 /*        return (fix)(((double)a * 65536.0) / (double)b);*/
118         return (fix)((((QLONG)a) << 16)/b);
119 /*        register fix ret;
120         asm("mov %%eax,%%edx; sar $16,%%edx; shl $16,%%eax; idiv %%ebx" : "=a" (ret) : "a" (a), "b" (b) : "%edx");
121     return ret; */
122 }
123
124 fix fixmuldiv(fix a, fix b, fix c)
125 {
126 /*        register fix ret;
127         asm("imul %%edx; idiv %%ebx" : "=a" (ret) : "a" (a), "d" (b), "b" (c) : "%edx");
128     return ret;*/
129
130 /*        double d;
131
132         d = (double)a * (double) b;
133         return (fix)(d / (double) c);
134 */
135         return (fix)((((QLONG)a)*b)/c);
136 }
137 #endif
138
139 //given cos & sin of an angle, return that angle.
140 //parms need not be normalized, that is, the ratio of the parms cos/sin must
141 //equal the ratio of the actual cos & sin for the result angle, but the parms 
142 //need not be the actual cos & sin.  
143 //NOTE: this is different from the standard C atan2, since it is left-handed.
144
145 fixang fix_atan2(fix cos,fix sin)
146 {
147         double d, dsin, dcos;
148         fixang t;
149
150         //Assert(!(cos==0 && sin==0));
151
152         //find smaller of two
153
154         dsin = (double)sin;
155         dcos = (double)cos;
156         d = sqrt((dsin * dsin) + (dcos * dcos));
157
158         if (d==0.0)
159                 return 0;
160
161         if (labs(sin) < labs(cos)) {                            //sin is smaller, use arcsin
162                 t = fix_asin((fix)((dsin / d) * 65536.0));
163                 if (cos<0)
164                         t = 0x8000 - t;
165                 return t;
166         }
167         else {
168                 t = fix_acos((fix)((dcos / d) * 65536.0));
169                 if (sin<0)
170                         t = -t;
171                 return t;
172         }
173 }
174
175 #ifdef NO_FIX_INLINE
176 //divide a quadint by a fix, returning a fix
177 int32_t fixdivquadlong(uint32_t nl, uint32_t nh, uint32_t d)
178 {
179         int i;
180         uint32_t tmp0;
181         ubyte tmp1;
182         uint32_t r;
183         ubyte T,Q,M;
184
185         r = 0;
186
187         Q = ((nh&0x80000000)!=0);
188         M = ((d&0x80000000)!=0);
189         T = (M!=Q);
190
191         if (M == 0)
192         {
193                 for (i=0; i<32; i++ )   {
194         
195                         r <<= 1;
196                         r |= T;
197                         T = ((nl&0x80000000L)!=0);
198                         nl <<= 1;
199         
200                         switch( Q ) {
201                 
202                         case 0:
203                                 Q = (unsigned char)((0x80000000L & nh) != 0 );
204                                 nh = (nh << 1) | (uint32_t)T;
205
206                                 tmp0 = nh;
207                                 nh -= d;
208                                 tmp1 = (nh>tmp0);
209                                 if (Q == 0)
210                                         Q = tmp1;
211                                 else
212                                         Q = (unsigned char)(tmp1 == 0);
213                                 break;
214                         case 1:
215                                 Q = (unsigned char)((0x80000000L & nh) != 0 );
216                                 nh = (nh << 1) | (uint32_t)T;
217
218                                 tmp0 = nh;
219                                 nh += d;
220                                 tmp1 = (nh<tmp0);
221                                 if (Q == 0)
222                                         Q = tmp1;
223                                 else
224                                         Q = (unsigned char)(tmp1 == 0);
225                                 break;
226                         }
227                         T = (Q==M);
228                 }
229         }
230         else
231         {
232                 for (i=0; i<32; i++ )   {
233         
234                         r <<= 1;
235                         r |= T;
236                         T = ((nl&0x80000000L)!=0);
237                         nl <<= 1;
238         
239                         switch( Q ) {
240                 
241                         case 0:
242                                 Q = (unsigned char)((0x80000000L & nh) != 0 );
243                                 nh = (nh << 1) | (uint32_t)T;
244
245                                 tmp0 = nh;
246                                 nh += d;
247                                 tmp1 = (nh<tmp0);
248                                 if (Q == 1)
249                                         Q = tmp1;
250                                 else
251                                         Q = (unsigned char)(tmp1 == 0);
252                                 break;
253                         case 1: 
254                                 Q = (unsigned char)((0x80000000L & nh) != 0 );
255                                 nh = (nh << 1) | (uint32_t)T;
256
257                                 tmp0 = nh;
258                                 nh = nh - d;
259                                 tmp1 = (nh>tmp0);
260                                 if (Q == 1)
261                                         Q = tmp1;
262                                 else
263                                         Q = (unsigned char)(tmp1 == 0);
264                                 break;
265                         }
266                         T = (Q==M);
267                 }
268         }
269
270         r = (r << 1) | T;
271
272         return r;
273 }
274
275 #if 0
276 // this version caused inf loop with:
277 // quad_sqrt(0x27eb7121/*low=669741345*/,
278 //           0x4cd40ad8/*high=1288964824*/);
279 unsigned int fixdivquadlongu(uint nl, uint nh, uint d)
280 {
281   return fixdivquadlong((uint32_t) nl,(uint32_t) nh,(uint32_t) d);
282 }
283 #endif
284
285 unsigned int fixdivquadlongu(uint nl, uint nh, uint d)
286 {
287   uint64_t n = (uint64_t)nl | (((uint64_t)nh) << 32 );
288   return n / ((uint64_t)d);
289 }
290
291 #else //of ifdef NO_FIX_INLINE
292 int32_t fixdivquadlong(uint32_t nl, uint32_t nh, uint32_t d)
293 {
294 int32_t a;
295 __asm__("idivl %3"
296          :"=a" (a)
297          :"a" (nl), "d" (nh), "r" (d)
298          :"ax", "dx"
299          );
300 return (a);
301 }
302 static inline uint32_t fixdivquadlongu(uint32_t nl, uint32_t nh, uint32_t d)
303 {
304 uint32_t a;
305 __asm__("divl %3"
306          :"=a" (a)
307          :"a" (nl), "d" (nh), "r" (d)
308          :"ax", "dx"
309          );
310 return (a);
311 }
312 #endif //def NO_FIX_INLINE
313
314 uint32_t quad_sqrt(uint32_t low, int32_t high)
315 {
316         int i, cnt;
317         uint32_t r, old_r, t;
318         quadint tq;
319
320         if (high<0)
321                 return 0;
322
323         if (high==0 && (int32_t)low>=0)
324                 return long_sqrt((int32_t)low);
325
326         if (high & 0xff000000) {
327                 cnt=12+16; i = high >> 24;
328         } else if (high & 0xff0000) {
329                 cnt=8+16; i = high >> 16;
330         } else if (high & 0xff00) {
331                 cnt=4+16; i = high >> 8;
332         } else {
333                 cnt=0+16; i = high;
334         }
335         
336         r = guess_table[i]<<cnt;
337
338         //quad loop usually executed 4 times
339
340         r = fixdivquadlongu(low,high,r)/2 + r/2;
341         r = fixdivquadlongu(low,high,r)/2 + r/2;
342         r = fixdivquadlongu(low,high,r)/2 + r/2;
343
344         do {
345
346                 old_r = r;
347                 t = fixdivquadlongu(low,high,r);
348
349                 if (t==r)       //got it!
350                         return r;
351  
352                 r = t/2 + r/2;
353
354         } while (!(r==t || r==old_r));
355
356         t = fixdivquadlongu(low,high,r);
357         //edited 05/17/99 Matt Mueller - tq.high is undefined here.. so set them to = 0
358         tq.low=tq.high=0;
359         //end edit -MM
360         fixmulaccum(&tq,r,t);
361         if (tq.low!=low || tq.high!=high)
362                 r++;
363
364         return r;
365 }
366
367 //computes the square root of a long, returning a short
368 ushort long_sqrt(int32_t a)
369 {
370         int cnt,r,old_r,t;
371
372         if (a<=0)
373                 return 0;
374
375         if (a & 0xff000000)
376                 cnt=12;
377         else if (a & 0xff0000)
378                 cnt=8;
379         else if (a & 0xff00)
380                 cnt=4;
381         else
382                 cnt=0;
383         
384         r = guess_table[(a>>cnt)&0xff]<<cnt;
385
386         //the loop nearly always executes 3 times, so we'll unroll it 2 times and
387         //not do any checking until after the third time.  By my calcutations, the
388         //loop is executed 2 times in 99.97% of cases, 3 times in 93.65% of cases, 
389         //four times in 16.18% of cases, and five times in 0.44% of cases.  It never
390         //executes more than five times.  By timing, I determined that is is faster
391         //to always execute three times and not check for termination the first two
392         //times through.  This means that in 93.65% of cases, we save 6 cmp/jcc pairs,
393         //and in 6.35% of cases we do an extra divide.  In real life, these numbers
394         //might not be the same.
395
396         r = ((a/r)+r)/2;
397         r = ((a/r)+r)/2;
398
399         do {
400
401                 old_r = r;
402                 t = a/r;
403
404                 if (t==r)       //got it!
405                         return r;
406  
407                 r = (t+r)/2;
408
409         } while (!(r==t || r==old_r));
410
411         if (a % r)
412                 r++;
413
414         return r;
415 }
416
417 //computes the square root of a fix, returning a fix
418 fix fix_sqrt(fix a)
419 {
420         return ((fix) long_sqrt(a)) << 8;
421 }
422
423
424 //compute sine and cosine of an angle, filling in the variables
425 //either of the pointers can be NULL
426 //with interpolation
427 void fix_sincos(fix a,fix *s,fix *c)
428 {
429         int i,f;
430         fix ss,cc;
431
432         i = (a>>8)&0xff;
433         f = a&0xff;
434
435         ss = sincos_table[i];
436         if (s) *s = (ss + (((sincos_table[i+1] - ss) * f)>>8))<<2;
437
438         cc = sincos_table[i+64];
439         if (c) *c = (cc + (((sincos_table[i+64+1] - cc) * f)>>8))<<2;
440 }
441
442 //compute sine and cosine of an angle, filling in the variables
443 //either of the pointers can be NULL
444 //no interpolation
445 void fix_fastsincos(fix a,fix *s,fix *c)
446 {
447         int i;
448
449         i = (a>>8)&0xff;
450
451         if (s) *s = sincos_table[i] << 2;
452         if (c) *c = sincos_table[i+64] << 2;
453 }
454
455 //compute inverse sine
456 fixang fix_asin(fix v)
457 {
458         fix vv;
459         int i,f,aa;
460
461         vv = labs(v);
462
463         if (vv >= f1_0)         //check for out of range
464                 return 0x4000;
465
466         i = (vv>>8)&0xff;
467         f = vv&0xff;
468
469         aa = asin_table[i];
470         aa = aa + (((asin_table[i+1] - aa) * f)>>8);
471
472         if (v < 0)
473                 aa = -aa;
474
475         return aa;
476 }
477
478 //compute inverse cosine
479 fixang fix_acos(fix v)
480 {
481         fix vv;
482         int i,f,aa;
483
484         vv = labs(v);
485
486         if (vv >= f1_0)         //check for out of range
487                 return 0;
488
489         i = (vv>>8)&0xff;
490         f = vv&0xff;
491
492         aa = acos_table[i];
493         aa = aa + (((acos_table[i+1] - aa) * f)>>8);
494
495         if (v < 0)
496                 aa = 0x8000 - aa;
497
498         return aa;
499 }
500
501 #define TABLE_SIZE 1024
502
503 //for passed value a, returns 1/sqrt(a) 
504 fix fix_isqrt( fix a )
505 {
506         int i, b = a;
507         int cnt = 0;
508         int r;
509
510         if ( a == 0 ) return 0;
511
512         while( b >= TABLE_SIZE )        {
513                 b >>= 1;
514                 cnt++;
515         }
516
517         //printf( "Count = %d (%d>>%d)\n", cnt, b, (cnt+1)/2 );
518         r = isqrt_guess_table[b] >> ((cnt+1)/2);
519
520         //printf( "Initial r = %d\n", r );
521
522         for (i=0; i<3; i++ )    {
523                 int old_r = r;
524                 r = fixmul( ( (3*65536) - fixmul(fixmul(r,r),a) ), r) / 2;
525                 //printf( "r %d  = %d\n", i, r );
526                 if ( old_r >= r ) return (r+old_r)/2;
527         }
528
529         return r;       
530 }