Skip to content

Commit ad33474

Browse files
committed
Enhance Precision of Fixed-Point Square Root Computations
The current square root calculation for fixed-point numbers cannot handle values less than 1 accurately. To improve precision, replace the existing method with the digit-by-digit calculation method. This approach, combined with offset manipulation, will minimize precision loss during the square root calculation.
1 parent f38d65c commit ad33474

File tree

1 file changed

+53
-29
lines changed

1 file changed

+53
-29
lines changed

src/fixed.c

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,42 +9,66 @@
99
#define uint32_lo(i) ((i) & 0xffff)
1010
#define uint32_hi(i) ((i) >> 16)
1111
#define uint32_carry16 ((1) << 16)
12+
/*if (x > minx - epsilon && x < minx + epsilon) ...*/
13+
#define CHECK_INTERVAL(x, minx, epsilon) \
14+
((int32_t) ((x - (minx - epsilon)) | (minx + epsilon - x)) > 0)
1215

1316
twin_fixed_t twin_fixed_sqrt(twin_fixed_t a)
1417
{
15-
twin_fixed_t max = a, min = 0;
16-
17-
while (max > min) {
18-
twin_fixed_t mid = (max + min) >> 1;
19-
if (mid >= 181 * TWIN_FIXED_ONE) {
20-
max = mid - 1;
21-
continue;
22-
}
23-
twin_fixed_t sqr = twin_fixed_mul(mid, mid);
24-
if (sqr == a)
25-
return mid;
26-
if (sqr < a)
27-
min = mid + 1;
28-
else
29-
max = mid - 1;
18+
if (a <= 0)
19+
return 0;
20+
21+
if (CHECK_INTERVAL(a, TWIN_FIXED_ONE, (1 << 8-1)))
22+
return TWIN_FIXED_ONE;
23+
24+
// count leading zero
25+
int offset = 0;
26+
for (twin_fixed_t i = a; !(0x40000000 & i); i <<= 1) {
27+
++offset;
28+
}
29+
// shift left 'a' to expand more digit for sqrt precision
30+
offset &= ~1;
31+
a <<= offset;
32+
// calculate the digits need to shift back
33+
offset >>= 1;
34+
offset -= (16 >> 1);
35+
//calculate sqrt
36+
twin_fixed_t z = 0;
37+
for (twin_fixed_t m = 1UL << ((31 - __builtin_clz(a)) & ~1UL); m; m >>= 2) {
38+
int b = z + m;
39+
z >>= 1;
40+
if (a >= b)
41+
a -= b, z += m;
3042
}
31-
return (max + min) >> 1;
43+
// shift back the expanded digits
44+
return (offset >= 0) ? z >> offset : z << (-offset);
3245
}
3346

3447
twin_sfixed_t _twin_sfixed_sqrt(twin_sfixed_t as)
3548
{
36-
twin_dfixed_t max = as, min = 0;
37-
twin_dfixed_t a = twin_sfixed_to_dfixed(as);
38-
39-
while (max > min) {
40-
twin_dfixed_t mid = (max + min) >> 1;
41-
twin_dfixed_t sqr = mid * mid;
42-
if (sqr == a)
43-
return (twin_sfixed_t) mid;
44-
if (sqr < a)
45-
min = mid + 1;
46-
else
47-
max = mid - 1;
49+
if (as <= 0)
50+
return 0;
51+
if (CHECK_INTERVAL(as, TWIN_SFIXED_ONE, (1 << 2-1)))
52+
return TWIN_SFIXED_ONE;
53+
54+
int offset = 0;
55+
for (twin_sfixed_t i = as; !(0x4000 & i); i <<= 1) {
56+
++offset;
4857
}
49-
return (twin_sfixed_t) ((max + min) >> 1);
58+
offset &= ~1;
59+
as <<= offset;
60+
61+
offset >>= 1;
62+
offset -= (4 >> 1);
63+
64+
twin_sfixed_t z = 0;
65+
for (twin_sfixed_t m = 1UL << ((31 - __builtin_clz(as)) & ~1UL); m;
66+
m >>= 2) {
67+
int16_t b = z + m;
68+
z >>= 1;
69+
if (as >= b)
70+
as -= b, z += m;
71+
}
72+
73+
return (offset >= 0) ? z >> offset : z << (-offset);
5074
}

0 commit comments

Comments
 (0)