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