202 lines
4.6 KiB
C
202 lines
4.6 KiB
C
/* fixed.c - Q16.16 Fixed-Point Math Implementation */
|
|
|
|
#include "fixed.h"
|
|
|
|
/* Conversion functions */
|
|
fixed_t int_to_fixed(int32_t i) {
|
|
return i << 16;
|
|
}
|
|
|
|
int32_t fixed_to_int(fixed_t f) {
|
|
return f >> 16;
|
|
}
|
|
|
|
fixed_t float_to_fixed(float f) {
|
|
return (fixed_t)(f * 65536.0f);
|
|
}
|
|
|
|
float fixed_to_float(fixed_t f) {
|
|
return (float)f / 65536.0f;
|
|
}
|
|
|
|
fixed_t fixed_add(fixed_t a, fixed_t b) {
|
|
return a + b;
|
|
}
|
|
|
|
fixed_t fixed_sub(fixed_t a, fixed_t b) {
|
|
return a - b;
|
|
}
|
|
|
|
fixed_t fixed_mul(fixed_t a, fixed_t b) {
|
|
/* Extract high and low parts */
|
|
int32_t a_hi = a >> 16;
|
|
uint32_t a_lo = (uint32_t)a & 0xFFFFU;
|
|
int32_t b_hi = b >> 16;
|
|
uint32_t b_lo = (uint32_t)b & 0xFFFFU;
|
|
|
|
/* Compute partial products */
|
|
int32_t p0 = (int32_t)(a_lo * b_lo) >> 16; /* Low * Low */
|
|
int32_t p1 = a_hi * (int32_t)b_lo; /* High * Low */
|
|
int32_t p2 = (int32_t)a_lo * b_hi; /* Low * High */
|
|
int32_t p3 = (a_hi * b_hi) << 16; /* High * High */
|
|
|
|
/* Combine results */
|
|
return p0 + p1 + p2 + p3;
|
|
}
|
|
|
|
fixed_t fixed_div(fixed_t a, fixed_t b) {
|
|
if (b == 0) return 0; /* Handle division by zero */
|
|
|
|
/* Determine sign */
|
|
int negative = ((a < 0) ^ (b < 0));
|
|
|
|
/* Work with absolute values */
|
|
uint32_t ua = (a < 0) ? -a : a;
|
|
uint32_t ub = (b < 0) ? -b : b;
|
|
|
|
/* Perform division using long division in base 2^16 */
|
|
uint32_t quotient = 0;
|
|
uint32_t remainder = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
remainder <<= 1;
|
|
if (ua & 0x80000000U) {
|
|
remainder |= 1;
|
|
}
|
|
ua <<= 1;
|
|
|
|
if (remainder >= ub) {
|
|
remainder -= ub;
|
|
quotient |= 1;
|
|
}
|
|
|
|
if (i < 31) {
|
|
quotient <<= 1;
|
|
}
|
|
}
|
|
|
|
return negative ? -(int32_t)quotient : (int32_t)quotient;
|
|
}
|
|
|
|
int fixed_eq(fixed_t a, fixed_t b) {
|
|
return a == b;
|
|
}
|
|
|
|
int fixed_ne(fixed_t a, fixed_t b) {
|
|
return a != b;
|
|
}
|
|
|
|
int fixed_lt(fixed_t a, fixed_t b) {
|
|
return a < b;
|
|
}
|
|
|
|
int fixed_le(fixed_t a, fixed_t b) {
|
|
return a <= b;
|
|
}
|
|
|
|
int fixed_gt(fixed_t a, fixed_t b) {
|
|
return a > b;
|
|
}
|
|
|
|
int fixed_ge(fixed_t a, fixed_t b) {
|
|
return a >= b;
|
|
}
|
|
|
|
/* Unary operations */
|
|
fixed_t fixed_neg(fixed_t f) {
|
|
return -f;
|
|
}
|
|
|
|
fixed_t fixed_abs(fixed_t f) {
|
|
return (f < 0) ? -f : f;
|
|
}
|
|
|
|
/* Square root using Newton-Raphson method */
|
|
fixed_t fixed_sqrt(fixed_t f) {
|
|
if (f <= 0) return 0;
|
|
|
|
fixed_t x = f;
|
|
fixed_t prev;
|
|
|
|
/* Newton-Raphson iteration: x = (x + f/x) / 2 */
|
|
do {
|
|
prev = x;
|
|
x = fixed_div(fixed_add(x, fixed_div(f, x)), int_to_fixed(2));
|
|
} while (fixed_gt(fixed_abs(fixed_sub(x, prev)), 1)); /* Precision to 1/65536 */
|
|
|
|
return x;
|
|
}
|
|
|
|
/* Sine function using Taylor series */
|
|
fixed_t fixed_sin(fixed_t f) {
|
|
/* Normalize angle to [-π, π] */
|
|
fixed_t pi2 = fixed_mul(FIXED_PI, int_to_fixed(2));
|
|
while (fixed_gt(f, FIXED_PI)) f = fixed_sub(f, pi2);
|
|
while (fixed_lt(f, fixed_neg(FIXED_PI))) f = fixed_add(f, pi2);
|
|
|
|
/* Taylor series: sin(x) = x - x³/3! + x⁵/5! - x⁷/7! + ... */
|
|
fixed_t result = f;
|
|
fixed_t term = f;
|
|
fixed_t f_squared = fixed_mul(f, f);
|
|
|
|
/* Calculate first few terms for reasonable precision */
|
|
int i;
|
|
for (i = 3; i <= 11; i += 2) {
|
|
term = fixed_mul(term, f_squared);
|
|
term = fixed_div(term, int_to_fixed(i * (i - 1)));
|
|
|
|
if ((i / 2) % 2 == 0) {
|
|
result = fixed_add(result, term);
|
|
} else {
|
|
result = fixed_sub(result, term);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Cosine function using Taylor series */
|
|
fixed_t fixed_cos(fixed_t f) {
|
|
/* cos(x) = 1 - x²/2! + x⁴/4! - x⁶/6! + ... */
|
|
fixed_t result = FIXED_ONE;
|
|
fixed_t term = FIXED_ONE;
|
|
fixed_t f_squared = fixed_mul(f, f);
|
|
|
|
int i;
|
|
for (i = 2; i <= 12; i += 2) {
|
|
term = fixed_mul(term, f_squared);
|
|
term = fixed_div(term, int_to_fixed(i * (i - 1)));
|
|
|
|
if ((i / 2) % 2 == 0) {
|
|
result = fixed_add(result, term);
|
|
} else {
|
|
result = fixed_sub(result, term);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Tangent function */
|
|
fixed_t fixed_tan(fixed_t f) {
|
|
fixed_t cos_val = fixed_cos(f);
|
|
if (cos_val == 0) return 0; /* Handle undefined case */
|
|
return fixed_div(fixed_sin(f), cos_val);
|
|
}
|
|
|
|
/* Utility functions */
|
|
fixed_t fixed_min(fixed_t a, fixed_t b) {
|
|
return (a < b) ? a : b;
|
|
}
|
|
|
|
fixed_t fixed_max(fixed_t a, fixed_t b) {
|
|
return (a > b) ? a : b;
|
|
}
|
|
|
|
fixed_t fixed_clamp(fixed_t f, fixed_t min_val, fixed_t max_val) {
|
|
if (f < min_val) return min_val;
|
|
if (f > max_val) return max_val;
|
|
return f;
|
|
}
|