fixedptc.h


1
#ifndef _FIXEDPTC_H_
2
#define _FIXEDPTC_H_
3
4
/*
5
 * fixedptc.h is a 32-bit or 64-bit fixed point numeric library.
6
 *
7
 * The symbol FIXEDPT_BITS, if defined before this library header file
8
 * is included, governs the number of bits in the data type (its "width").
9
 * The default width is 32-bit (FIXEDPT_BITS=32) and it can be used
10
 * on any recent C99 compiler. The 64-bit precision (FIXEDPT_BITS=64) is
11
 * available on compilers which implement 128-bit "long long" types. This
12
 * precision has been tested on GCC 4.2+.
13
 *
14
 * Since the precision in both cases is relatively low, many complex
15
 * functions (more complex than div & mul) take a large hit on the precision
16
 * of the end result because errors in precision accumulate.
17
 * This loss of precision can be lessened by increasing the number of
18
 * bits dedicated to the fraction part, but at the loss of range.
19
 *
20
 * Adventurous users might utilize this library to build two data types:
21
 * one which has the range, and one which has the precision, and carefully
22
 * convert between them (including adding two number of each type to produce
23
 * a simulated type with a larger range and precision).
24
 *
25
 * The ideas and algorithms have been cherry-picked from a large number
26
 * of previous implementations available on the Internet.
27
 * Tim Hartrick has contributed cleanup and 64-bit support patches.
28
 *
29
 * == Special notes for the 32-bit precision ==
30
 * Signed 32-bit fixed point numeric library for the 24.8 format.
31
 * The specific limits are -8388608.999... to 8388607.999... and the
32
 * most precise number is 0.00390625. In practice, you should not count
33
 * on working with numbers larger than a million or to the precision
34
 * of more than 2 decimal places. Make peace with the fact that PI
35
 * is 3.14 here. :)
36
 */
37
38
/*-
39
 * Copyright (c) 2010-2012 Ivan Voras <ivoras@freebsd.org>
40
 * Copyright (c) 2012 Tim Hartrick <tim@edgecast.com>
41
 *
42
 * Redistribution and use in source and binary forms, with or without
43
 * modification, are permitted provided that the following conditions
44
 * are met:
45
 * 1. Redistributions of source code must retain the above copyright
46
 *    notice, this list of conditions and the following disclaimer.
47
 * 2. Redistributions in binary form must reproduce the above copyright
48
 *    notice, this list of conditions and the following disclaimer in the
49
 *    documentation and/or other materials provided with the distribution.
50
 *
51
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
52
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
55
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61
 * SUCH DAMAGE.
62
 */
63
64
#ifndef FIXEDPT_BITS
65
#define FIXEDPT_BITS  32
66
#endif
67
68
#if FIXEDPT_BITS == 32
69
typedef int32_t fixedpt;
70
typedef  int64_t  fixedptd;
71
typedef  uint32_t fixedptu;
72
typedef  uint64_t fixedptud;
73
#elif FIXEDPT_BITS == 64
74
typedef int64_t fixedpt;
75
typedef  __int128_t fixedptd;
76
typedef  uint64_t fixedptu;
77
typedef  __uint128_t fixedptud;
78
#else
79
#error "FIXEDPT_BITS must be equal to 32 or 64"
80
#endif
81
82
#ifndef FIXEDPT_WBITS
83
#define FIXEDPT_WBITS  24
84
#endif
85
86
#if FIXEDPT_WBITS >= FIXEDPT_BITS
87
#error "FIXEDPT_WBITS must be less than or equal to FIXEDPT_BITS"
88
#endif
89
90
#define FIXEDPT_VCSID "$Id: fixedptc.h,v 00c74d842389 2012/07/17 23:30:18 ivoras $"
91
92
#define FIXEDPT_FBITS  (FIXEDPT_BITS - FIXEDPT_WBITS)
93
#define FIXEDPT_FMASK  (((fixedpt)1 << FIXEDPT_FBITS) - 1)
94
95
#define fixedpt_rconst(R) ((fixedpt)((R) * (((fixedptd)1 << FIXEDPT_FBITS) \
96
  + ((R) >= 0 ? 0.5 : -0.5))))
97
#define fixedpt_fromint(I) ((fixedptd)(I) << FIXEDPT_FBITS)
98
#define fixedpt_toint(F) ((F) >> FIXEDPT_FBITS)
99
#define fixedpt_add(A,B) ((A) + (B))
100
#define fixedpt_sub(A,B) ((A) - (B))
101
#define fixedpt_xmul(A,B)            \
102
  ((fixedpt)(((fixedptd)(A) * (fixedptd)(B)) >> FIXEDPT_FBITS))
103
#define fixedpt_xdiv(A,B)            \
104
  ((fixedpt)(((fixedptd)(A) << FIXEDPT_FBITS) / (fixedptd)(B)))
105
#define fixedpt_fracpart(A) ((fixedpt)(A) & FIXEDPT_FMASK)
106
107
#define FIXEDPT_ONE  ((fixedpt)((fixedpt)1 << FIXEDPT_FBITS))
108
#define FIXEDPT_ONE_HALF (FIXEDPT_ONE >> 1)
109
#define FIXEDPT_TWO  (FIXEDPT_ONE + FIXEDPT_ONE)
110
#define FIXEDPT_PI  fixedpt_rconst(3.14159265358979323846)
111
#define FIXEDPT_TWO_PI  fixedpt_rconst(2 * 3.14159265358979323846)
112
#define FIXEDPT_HALF_PI  fixedpt_rconst(3.14159265358979323846 / 2)
113
#define FIXEDPT_E  fixedpt_rconst(2.7182818284590452354)
114
115
#define fixedpt_abs(A) ((A) < 0 ? -(A) : (A))
116
117
118
/* Multiplies two fixedpt numbers, returns the result. */
119
static inline fixedpt
120
fixedpt_mul(fixedpt A, fixedpt B)
121
{
122
  return (((fixedptd)A * (fixedptd)B) >> FIXEDPT_FBITS);
123
}
124
125
126
/* Divides two fixedpt numbers, returns the result. */
127
static inline fixedpt
128
fixedpt_div(fixedpt A, fixedpt B)
129
{
130
  return (((fixedptd)A << FIXEDPT_FBITS) / (fixedptd)B);
131
}
132
133
/*
134
 * Note: adding and substracting fixedpt numbers can be done by using
135
 * the regular integer operators + and -.
136
 */
137
138
/**
139
 * Convert the given fixedpt number to a decimal string.
140
 * The max_dec argument specifies how many decimal digits to the right
141
 * of the decimal point to generate. If set to -1, the "default" number
142
 * of decimal digits will be used (2 for 32-bit fixedpt width, 10 for
143
 * 64-bit fixedpt width); If set to -2, "all" of the digits will
144
 * be returned, meaning there will be invalid, bogus digits outside the
145
 * specified precisions.
146
 */
147
static inline void
148
fixedpt_str(fixedpt A, char *str, int max_dec)
149
{
150
  int ndec = 0, slen = 0;
151
  char tmp[12] = {0};
152
  fixedptud fr, ip;
153
  const fixedptud one = (fixedptud)1 << FIXEDPT_BITS;
154
  const fixedptud mask = one - 1;
155
156
  if (max_dec == -1)
157
#if FIXEDPT_BITS == 32
158
    max_dec = 2;
159
#elif FIXEDPT_BITS == 64
160
    max_dec = 10;
161
#else
162
#error Invalid width
163
#endif
164
  else if (max_dec == -2)
165
    max_dec = 15;
166
167
  if (A < 0) {
168
    str[slen++] = '-';
169
    A *= -1;
170
  }
171
172
  ip = fixedpt_toint(A);
173
  do {
174
    tmp[ndec++] = '0' + ip % 10;
175
    ip /= 10;
176
  } while (ip != 0);
177
178
  while (ndec > 0)
179
    str[slen++] = tmp[--ndec];
180
  str[slen++] = '.';
181
182
  fr = (fixedpt_fracpart(A) << FIXEDPT_WBITS) & mask;
183
  do {
184
    fr = (fr & mask) * 10;
185
186
    str[slen++] = '0' + (fr >> FIXEDPT_BITS) % 10;
187
    ndec++;
188
  } while (fr != 0 && ndec < max_dec);
189
190
  if (ndec > 1 && str[slen-1] == '0')
191
    str[slen-1] = '\0'; /* cut off trailing 0 */
192
  else
193
    str[slen] = '\0';
194
}
195
196
/* Converts the given fixedpt number into a string, using a static
197
 * (non-threadsafe) string buffer */
198
static inline char*
199
fixedpt_cstr(const fixedpt A, const int max_dec)
200
{
201
  static char str[25];
202
203
  fixedpt_str(A, str, max_dec);
204
  return (str);
205
}
206
207
208
/* Returns the square root of the given number, or -1 in case of error */
209
static inline fixedpt
210
fixedpt_sqrt(fixedpt A)
211
{
212
  int invert = 0;
213
  int iter = FIXEDPT_FBITS;
214
  int l, i;
215
216
  if (A < 0)
217
    return (-1);
218
  if (A == 0 || A == FIXEDPT_ONE)
219
    return (A);
220
  if (A < FIXEDPT_ONE && A > 6) {
221
    invert = 1;
222
    A = fixedpt_div(FIXEDPT_ONE, A);
223
  }
224
  if (A > FIXEDPT_ONE) {
225
    int s = A;
226
227
    iter = 0;
228
    while (s > 0) {
229
      s >>= 2;
230
      iter++;
231
    }
232
  }
233
234
  /* Newton's iterations */
235
  l = (A >> 1) + 1;
236
  for (i = 0; i < iter; i++)
237
    l = (l + fixedpt_div(A, l)) >> 1;
238
  if (invert)
239
    return (fixedpt_div(FIXEDPT_ONE, l));
240
  return (l);
241
}
242
243
244
/* Returns the sine of the given fixedpt number. 
245
 * Note: the loss of precision is extraordinary! */
246
static inline fixedpt
247
fixedpt_sin(fixedpt fp)
248
{
249
  int sign = 1;
250
  fixedpt sqr, result;
251
  const fixedpt SK[2] = {
252
    fixedpt_rconst(7.61e-03),
253
    fixedpt_rconst(1.6605e-01)
254
  };
255
256
  fp %= 2 * FIXEDPT_PI;
257
  if (fp < 0)
258
    fp = FIXEDPT_PI * 2 + fp;
259
  if ((fp > FIXEDPT_HALF_PI) && (fp <= FIXEDPT_PI)) 
260
    fp = FIXEDPT_PI - fp;
261
  else if ((fp > FIXEDPT_PI) && (fp <= (FIXEDPT_PI + FIXEDPT_HALF_PI))) {
262
    fp = fp - FIXEDPT_PI;
263
    sign = -1;
264
  } else if (fp > (FIXEDPT_PI + FIXEDPT_HALF_PI)) {
265
    fp = (FIXEDPT_PI << 1) - fp;
266
    sign = -1;
267
  }
268
  sqr = fixedpt_mul(fp, fp);
269
  result = SK[0];
270
  result = fixedpt_mul(result, sqr);
271
  result -= SK[1];
272
  result = fixedpt_mul(result, sqr);
273
  result += FIXEDPT_ONE;
274
  result = fixedpt_mul(result, fp);
275
  return sign * result;
276
}
277
278
279
/* Returns the cosine of the given fixedpt number */
280
static inline fixedpt
281
fixedpt_cos(fixedpt A)
282
{
283
  return (fixedpt_sin(FIXEDPT_HALF_PI - A));
284
}
285
286
287
/* Returns the tangens of the given fixedpt number */
288
static inline fixedpt
289
fixedpt_tan(fixedpt A)
290
{
291
  return fixedpt_div(fixedpt_sin(A), fixedpt_cos(A));
292
}
293
294
295
/* Returns the value exp(x), i.e. e^x of the given fixedpt number. */
296
static inline fixedpt
297
fixedpt_exp(fixedpt fp)
298
{
299
  fixedpt xabs, k, z, R, xp;
300
  const fixedpt LN2 = fixedpt_rconst(0.69314718055994530942);
301
  const fixedpt LN2_INV = fixedpt_rconst(1.4426950408889634074);
302
  const fixedpt EXP_P[5] = {
303
    fixedpt_rconst(1.66666666666666019037e-01),
304
    fixedpt_rconst(-2.77777777770155933842e-03),
305
    fixedpt_rconst(6.61375632143793436117e-05),
306
    fixedpt_rconst(-1.65339022054652515390e-06),
307
    fixedpt_rconst(4.13813679705723846039e-08),
308
  };
309
310
  if (fp == 0)
311
    return (FIXEDPT_ONE);
312
  xabs = fixedpt_abs(fp);
313
  k = fixedpt_mul(xabs, LN2_INV);
314
  k += FIXEDPT_ONE_HALF;
315
  k &= ~FIXEDPT_FMASK;
316
  if (fp < 0)
317
    k = -k;
318
  fp -= fixedpt_mul(k, LN2);
319
  z = fixedpt_mul(fp, fp);
320
  /* Taylor */
321
  R = FIXEDPT_TWO +
322
      fixedpt_mul(z, EXP_P[0] + fixedpt_mul(z, EXP_P[1] +
323
      fixedpt_mul(z, EXP_P[2] + fixedpt_mul(z, EXP_P[3] +
324
      fixedpt_mul(z, EXP_P[4])))));
325
  xp = FIXEDPT_ONE + fixedpt_div(fixedpt_mul(fp, FIXEDPT_TWO), R - fp);
326
  if (k < 0)
327
    k = FIXEDPT_ONE >> (-k >> FIXEDPT_FBITS);
328
  else
329
    k = FIXEDPT_ONE << (k >> FIXEDPT_FBITS);
330
  return (fixedpt_mul(k, xp));
331
}
332
333
334
/* Returns the natural logarithm of the given fixedpt number. */
335
static inline fixedpt
336
fixedpt_ln(fixedpt x)
337
{
338
  fixedpt log2, xi;
339
  fixedpt f, s, z, w, R;
340
  const fixedpt LN2 = fixedpt_rconst(0.69314718055994530942);
341
  const fixedpt LG[7] = {
342
    fixedpt_rconst(6.666666666666735130e-01),
343
    fixedpt_rconst(3.999999999940941908e-01),
344
    fixedpt_rconst(2.857142874366239149e-01),
345
    fixedpt_rconst(2.222219843214978396e-01),
346
    fixedpt_rconst(1.818357216161805012e-01),
347
    fixedpt_rconst(1.531383769920937332e-01),
348
    fixedpt_rconst(1.479819860511658591e-01)
349
  };
350
351
  if (x < 0)
352
    return (0);
353
  if (x == 0)
354
    return 0xffffffff;
355
356
  log2 = 0;
357
  xi = x;
358
  while (xi > FIXEDPT_TWO) {
359
    xi >>= 1;
360
    log2++;
361
  }
362
  f = xi - FIXEDPT_ONE;
363
  s = fixedpt_div(f, FIXEDPT_TWO + f);
364
  z = fixedpt_mul(s, s);
365
  w = fixedpt_mul(z, z);
366
  R = fixedpt_mul(w, LG[1] + fixedpt_mul(w, LG[3]
367
      + fixedpt_mul(w, LG[5]))) + fixedpt_mul(z, LG[0]
368
      + fixedpt_mul(w, LG[2] + fixedpt_mul(w, LG[4]
369
      + fixedpt_mul(w, LG[6]))));
370
  return (fixedpt_mul(LN2, (log2 << FIXEDPT_FBITS)) + f
371
      - fixedpt_mul(s, f - R));
372
}
373
  
374
375
/* Returns the logarithm of the given base of the given fixedpt number */
376
static inline fixedpt
377
fixedpt_log(fixedpt x, fixedpt base)
378
{
379
  return (fixedpt_div(fixedpt_ln(x), fixedpt_ln(base)));
380
}
381
382
383
/* Return the power value (n^exp) of the given fixedpt numbers */
384
static inline fixedpt
385
fixedpt_pow(fixedpt n, fixedpt exp)
386
{
387
  if (exp == 0)
388
    return (FIXEDPT_ONE);
389
  if (n < 0)
390
    return 0;
391
  return (fixedpt_exp(fixedpt_mul(fixedpt_ln(n), exp)));
392
}
393
394
#endif