Skip to content

Commit 6503a9c

Browse files
committed
Implement precise square root calculation for fixed point
The original fixed point sqrt can not calculate sqrt of numbers less than 1. Implement a new sqrt calculation that is more precise and without fixed point multiplication.
1 parent f38d65c commit 6503a9c

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 && x < minx + size) ...*/
13+
#define RANGE_CHECK(x, minx, size) \
14+
((int32_t) ((x - minx) | (minx + size - 1 - 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 (RANGE_CHECK(a, TWIN_FIXED_ONE - (1 << (8 - 1)), (1 << 8)))
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 (RANGE_CHECK(as, TWIN_SFIXED_ONE - (1 << (2 - 1)), (1 << 2)))
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)