-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathfixed.c
123 lines (100 loc) · 3.2 KB
/
fixed.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*
* Twin - A Tiny Window System
* Copyright (c) 2004 Keith Packard <[email protected]>
* Copyright (c) 2024 National Cheng Kung University, Taiwan
* All rights reserved.
*/
#include "twin_private.h"
#define uint32_lo(i) ((i) & 0xffff)
#define uint32_hi(i) ((i) >> 16)
#define uint32_carry16 ((1) << 16)
/* Check interval
* For any variable interval checking:
* if (x > minx - epsilon && x < minx + epsilon) ...
* it is faster to use bit operation:
* if ((signed)((x - (minx - epsilon)) | ((minx + epsilon) - x)) > 0) ...
*/
#define CHECK_INTERVAL(x, minx, epsilon) \
((int32_t) ((x - (minx - epsilon)) | (minx + epsilon - x)) > 0)
#define CHECK_INTERVAL_64(x, minx, epsilon) \
((int64_t) ((x - (minx - epsilon)) | (minx + epsilon - x)) > 0)
twin_fixed_t twin_fixed_sqrt(twin_fixed_t a)
{
if (a <= 0)
return 0;
if (CHECK_INTERVAL(a, TWIN_FIXED_ONE, (1 << (8 - 1))))
return TWIN_FIXED_ONE;
/* Count leading zero */
int offset = 0;
for (twin_fixed_t i = a; !(0x40000000 & i); i <<= 1)
++offset;
/* Shift left 'a' to expand more digit for sqrt precision */
offset &= ~1;
a <<= offset;
/* Calculate the digits need to shift back */
offset >>= 1;
offset -= (16 >> 1);
/* Use digit-by-digit calculation to compute square root */
twin_fixed_t z = 0;
for (twin_fixed_t m = 1UL << ((31 - twin_clz(a)) & ~1UL); m; m >>= 2) {
int b = z + m;
z >>= 1;
if (a >= b)
a -= b, z += m;
}
/* Shift back the expanded digits */
return (offset >= 0) ? z >> offset : z << (-offset);
}
twin_sfixed_t _twin_sfixed_sqrt(twin_sfixed_t as)
{
if (as <= 0)
return 0;
if (CHECK_INTERVAL(as, TWIN_SFIXED_ONE, (1 << (2 - 1))))
return TWIN_SFIXED_ONE;
int offset = 0;
for (twin_sfixed_t i = as; !(0x4000 & i); i <<= 1)
++offset;
offset &= ~1;
as <<= offset;
offset >>= 1;
offset -= (4 >> 1);
twin_sfixed_t z = 0;
for (twin_sfixed_t m = 1UL << ((31 - twin_clz(as)) & ~1UL); m; m >>= 2) {
int16_t b = z + m;
z >>= 1;
if (as >= b)
as -= b, z += m;
}
return (offset >= 0) ? z >> offset : z << (-offset);
}
twin_xfixed_t _twin_xfixed_sqrt(twin_xfixed_t a)
{
/* Early return for non-positive inputs */
if (a <= 0)
return 0;
/* Special case for values very close to 1 */
if (CHECK_INTERVAL_64(a, TWIN_XFIXED_ONE, (1ULL << (16 - 1))))
return TWIN_XFIXED_ONE;
/* Count leading zero bits to normalize the input */
int64_t offset = 0;
for (twin_xfixed_t i = a; !(UINT64_C(0x4000000000000000) & i); i <<= 1)
++offset;
/* Ensure even offset for precision */
offset &= ~1;
/* Shift left to expand precision */
a <<= offset;
/* Calculate scaling offset */
offset >>= 1;
offset -= (32 >> 1);
/* Digit-by-digit square root calculation */
twin_xfixed_t z = 0;
for (twin_xfixed_t m = 1ULL << ((63 - __builtin_clzll(a)) & ~1ULL); m;
m >>= 2) {
twin_xfixed_t b = z + m;
z >>= 1;
if (a >= b)
a -= b, z += m;
}
/* Shift back the expanded digits */
return (offset >= 0) ? z >> offset : z << (-offset);
}