• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- Calculate square root of fixed point numbers. -----*- C++ -*-=========//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_LIBC_SRC___SUPPORT_FIXEDPOINT_SQRT_H
10 #define LLVM_LIBC_SRC___SUPPORT_FIXEDPOINT_SQRT_H
11 
12 #include "include/llvm-libc-macros/stdfix-macros.h"
13 #include "src/__support/CPP/bit.h"
14 #include "src/__support/CPP/limits.h" // CHAR_BIT
15 #include "src/__support/CPP/type_traits.h"
16 #include "src/__support/macros/attributes.h"   // LIBC_INLINE
17 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
18 
19 #include "fx_rep.h"
20 
21 #ifdef LIBC_COMPILER_HAS_FIXED_POINT
22 
23 namespace LIBC_NAMESPACE::fixed_point {
24 
25 namespace internal {
26 
27 template <typename T> struct SqrtConfig;
28 
29 template <> struct SqrtConfig<unsigned short fract> {
30   using Type = unsigned short fract;
31   static constexpr int EXTRA_STEPS = 0;
32 
33   // Linear approximation for the initial values, with errors bounded by:
34   //   max(1.5 * 2^-11, eps)
35   // Generated with Sollya:
36   // > for i from 4 to 15 do {
37   //     P = fpminimax(sqrt(x), 1, [|8, 8|], [i * 2^-4, (i + 1)*2^-4],
38   //                   fixed, absolute);
39   //     print("{", coeff(P, 1), "uhr,", coeff(P, 0), "uhr},");
40   //   };
41   static constexpr Type FIRST_APPROX[12][2] = {
42       {0x1.e8p-1uhr, 0x1.0cp-2uhr}, {0x1.bap-1uhr, 0x1.28p-2uhr},
43       {0x1.94p-1uhr, 0x1.44p-2uhr}, {0x1.74p-1uhr, 0x1.6p-2uhr},
44       {0x1.6p-1uhr, 0x1.74p-2uhr},  {0x1.4ep-1uhr, 0x1.88p-2uhr},
45       {0x1.3ep-1uhr, 0x1.9cp-2uhr}, {0x1.32p-1uhr, 0x1.acp-2uhr},
46       {0x1.22p-1uhr, 0x1.c4p-2uhr}, {0x1.18p-1uhr, 0x1.d4p-2uhr},
47       {0x1.08p-1uhr, 0x1.fp-2uhr},  {0x1.04p-1uhr, 0x1.f8p-2uhr},
48   };
49 };
50 
51 template <> struct SqrtConfig<unsigned fract> {
52   using Type = unsigned fract;
53   static constexpr int EXTRA_STEPS = 1;
54 
55   // Linear approximation for the initial values, with errors bounded by:
56   //   max(1.5 * 2^-11, eps)
57   // Generated with Sollya:
58   // > for i from 4 to 14 do {
59   //     P = fpminimax(sqrt(x), 1, [|16, 16|], [i * 2^-4, (i + 1)*2^-4],
60   //                   fixed, absolute);
61   //     print("{", coeff(P, 1), "ur,", coeff(P, 0), "ur},");
62   //   };
63   // For the last interval [15/16, 1), we choose the linear function Q such that
64   //   Q(1) = 1 and Q(15/16) = P(15/16),
65   // where P is the polynomial generated by Sollya above for [14/16, 15/16].
66   // This is to prevent overflow in the last interval [15/16, 1).
67   static constexpr Type FIRST_APPROX[12][2] = {
68       {0x1.e378p-1ur, 0x1.0ebp-2ur},  {0x1.b512p-1ur, 0x1.2b94p-2ur},
69       {0x1.91fp-1ur, 0x1.45dcp-2ur},  {0x1.7622p-1ur, 0x1.5e24p-2ur},
70       {0x1.5f5ap-1ur, 0x1.74e4p-2ur}, {0x1.4c58p-1ur, 0x1.8a4p-2ur},
71       {0x1.3c1ep-1ur, 0x1.9e84p-2ur}, {0x1.2e0cp-1ur, 0x1.b1d8p-2ur},
72       {0x1.21aap-1ur, 0x1.c468p-2ur}, {0x1.16bap-1ur, 0x1.d62cp-2ur},
73       {0x1.0cfp-1ur, 0x1.e74cp-2ur},  {0x1.039p-1ur, 0x1.f8ep-2ur},
74   };
75 };
76 
77 template <> struct SqrtConfig<unsigned long fract> {
78   using Type = unsigned long fract;
79   static constexpr int EXTRA_STEPS = 2;
80 
81   // Linear approximation for the initial values, with errors bounded by:
82   //   max(1.5 * 2^-11, eps)
83   // Generated with Sollya:
84   // > for i from 4 to 14 do {
85   //     P = fpminimax(sqrt(x), 1, [|32, 32|], [i * 2^-4, (i + 1)*2^-4],
86   //                   fixed, absolute);
87   //     print("{", coeff(P, 1), "ulr,", coeff(P, 0), "ulr},");
88   //   };
89   // For the last interval [15/16, 1), we choose the linear function Q such that
90   //   Q(1) = 1 and Q(15/16) = P(15/16),
91   // where P is the polynomial generated by Sollya above for [14/16, 15/16].
92   // This is to prevent overflow in the last interval [15/16, 1).
93   static constexpr Type FIRST_APPROX[12][2] = {
94       {0x1.e3779b98p-1ulr, 0x1.0eaff788p-2ulr},
95       {0x1.b5167872p-1ulr, 0x1.2b908ad4p-2ulr},
96       {0x1.91f195cap-1ulr, 0x1.45da800cp-2ulr},
97       {0x1.761ebcb4p-1ulr, 0x1.5e27004cp-2ulr},
98       {0x1.5f619986p-1ulr, 0x1.74db933cp-2ulr},
99       {0x1.4c583adep-1ulr, 0x1.8a3fbfccp-2ulr},
100       {0x1.3c1a591cp-1ulr, 0x1.9e88373cp-2ulr},
101       {0x1.2e08545ap-1ulr, 0x1.b1dd2534p-2ulr},
102       {0x1.21b05c0ap-1ulr, 0x1.c45e023p-2ulr},
103       {0x1.16becd02p-1ulr, 0x1.d624031p-2ulr},
104       {0x1.0cf49fep-1ulr, 0x1.e743b844p-2ulr},
105       {0x1.038cdfcp-1ulr, 0x1.f8e6408p-2ulr},
106   };
107 };
108 
109 template <>
110 struct SqrtConfig<unsigned short accum> : SqrtConfig<unsigned fract> {};
111 
112 template <>
113 struct SqrtConfig<unsigned accum> : SqrtConfig<unsigned long fract> {};
114 
115 // Integer square root
116 template <> struct SqrtConfig<unsigned short> {
117   using OutType = unsigned short accum;
118   using FracType = unsigned fract;
119   // For fast-but-less-accurate version
120   using FastFracType = unsigned short fract;
121   using HalfType = unsigned char;
122 };
123 
124 template <> struct SqrtConfig<unsigned int> {
125   using OutType = unsigned accum;
126   using FracType = unsigned long fract;
127   // For fast-but-less-accurate version
128   using FastFracType = unsigned fract;
129   using HalfType = unsigned short;
130 };
131 
132 // TODO: unsigned long accum type is 64-bit, and will need 64-bit fract type.
133 // Probably we will use DyadicFloat<64> for intermediate computations instead.
134 
135 } // namespace internal
136 
137 // Core computation for sqrt with normalized inputs (0.25 <= x < 1).
138 template <typename Config>
139 LIBC_INLINE constexpr typename Config::Type
140 sqrt_core(typename Config::Type x_frac) {
141   using FracType = typename Config::Type;
142   using FXRep = FXRep<FracType>;
143   using StorageType = typename FXRep::StorageType;
144   // Exact case:
145   if (x_frac == FXRep::ONE_FOURTH())
146     return FXRep::ONE_HALF();
147 
148   // Use use Newton method to approximate sqrt(a):
149   //   x_{n + 1} = 1/2 (x_n + a / x_n)
150   // For the initial values, we choose x_0
151 
152   // Use the leading 4 bits to do look up for sqrt(x).
153   // After normalization, 0.25 <= x_frac < 1, so the leading 4 bits of x_frac
154   // are between 0b0100 and 0b1111.  Hence the lookup table only needs 12
155   // entries, and we can get the index by subtracting the leading 4 bits of
156   // x_frac by 4 = 0b0100.
157   StorageType x_bit = cpp::bit_cast<StorageType>(x_frac);
158   int index = (static_cast<int>(x_bit >> (FXRep::TOTAL_LEN - 4))) - 4;
159   FracType a = Config::FIRST_APPROX[index][0];
160   FracType b = Config::FIRST_APPROX[index][1];
161 
162   // Initial approximation step.
163   // Estimated error bounds: | r - sqrt(x_frac) | < max(1.5 * 2^-11, eps).
164   FracType r = a * x_frac + b;
165 
166   // Further Newton-method iterations for square-root:
167   //   x_{n + 1} = 0.5 * (x_n + a / x_n)
168   // We distribute and do the multiplication by 0.5 first to avoid overflow.
169   // TODO: Investigate the performance and accuracy of using division-free
170   // iterations from:
171   //   Blanchard, J. D. and Chamberland, M., "Newton's Method Without Division",
172   //   The American Mathematical Monthly (2023).
173   //   https://chamberland.math.grinnell.edu/papers/newton.pdf
174   for (int i = 0; i < Config::EXTRA_STEPS; ++i)
175     r = (r >> 1) + (x_frac >> 1) / r;
176 
177   return r;
178 }
179 
180 template <typename T>
181 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T> sqrt(T x) {
182   using BitType = typename FXRep<T>::StorageType;
183   BitType x_bit = cpp::bit_cast<BitType>(x);
184 
185   if (LIBC_UNLIKELY(x_bit == 0))
186     return FXRep<T>::ZERO();
187 
188   int leading_zeros = cpp::countl_zero(x_bit);
189   constexpr int STORAGE_LENGTH = sizeof(BitType) * CHAR_BIT;
190   constexpr int EXP_ADJUSTMENT = STORAGE_LENGTH - FXRep<T>::FRACTION_LEN - 1;
191   // x_exp is the real exponent of the leading bit of x.
192   int x_exp = EXP_ADJUSTMENT - leading_zeros;
193   int shift = EXP_ADJUSTMENT - 1 - (x_exp & (~1));
194   // Normalize.
195   x_bit <<= shift;
196   using FracType = typename internal::SqrtConfig<T>::Type;
197   FracType x_frac = cpp::bit_cast<FracType>(x_bit);
198 
199   // Compute sqrt(x_frac) using Newton-method.
200   FracType r = sqrt_core<internal::SqrtConfig<T>>(x_frac);
201 
202   // Re-scaling
203   r >>= EXP_ADJUSTMENT - (x_exp >> 1);
204 
205   // Return result.
206   return cpp::bit_cast<T>(r);
207 }
208 
209 // Integer square root - Accurate version:
210 // Absolute errors < 2^(-fraction length).
211 template <typename T>
212 LIBC_INLINE constexpr typename internal::SqrtConfig<T>::OutType isqrt(T x) {
213   using OutType = typename internal::SqrtConfig<T>::OutType;
214   using FracType = typename internal::SqrtConfig<T>::FracType;
215 
216   if (x == 0)
217     return FXRep<OutType>::ZERO();
218 
219   // Normalize the leading bits to the first two bits.
220   // Shift and then Bit cast x to x_frac gives us:
221   //   x = 2^(FRACTION_LEN + 1 - shift) * x_frac;
222   int leading_zeros = cpp::countl_zero(x);
223   int shift = ((leading_zeros >> 1) << 1);
224   x <<= shift;
225   // Convert to frac type and compute square root.
226   FracType x_frac = cpp::bit_cast<FracType>(x);
227   FracType r = sqrt_core<internal::SqrtConfig<FracType>>(x_frac);
228   // To rescale back to the OutType (Accum)
229   r >>= (shift >> 1);
230 
231   return cpp::bit_cast<OutType>(r);
232 }
233 
234 // Integer square root - Fast but less accurate version:
235 // Relative errors < 2^(-fraction length).
236 template <typename T>
237 LIBC_INLINE constexpr typename internal::SqrtConfig<T>::OutType
238 isqrt_fast(T x) {
239   using OutType = typename internal::SqrtConfig<T>::OutType;
240   using FracType = typename internal::SqrtConfig<T>::FastFracType;
241   using StorageType = typename FXRep<FracType>::StorageType;
242 
243   if (x == 0)
244     return FXRep<OutType>::ZERO();
245 
246   // Normalize the leading bits to the first two bits.
247   // Shift and then Bit cast x to x_frac gives us:
248   //   x = 2^(FRACTION_LEN + 1 - shift) * x_frac;
249   int leading_zeros = cpp::countl_zero(x);
250   int shift = (leading_zeros & (~1));
251   x <<= shift;
252   // Convert to frac type and compute square root.
253   FracType x_frac = cpp::bit_cast<FracType>(
254       static_cast<StorageType>(x >> FXRep<FracType>::FRACTION_LEN));
255   OutType r =
256       static_cast<OutType>(sqrt_core<internal::SqrtConfig<FracType>>(x_frac));
257   // To rescale back to the OutType (Accum)
258   r <<= (FXRep<OutType>::INTEGRAL_LEN - (shift >> 1));
259   return cpp::bit_cast<OutType>(r);
260 }
261 
262 } // namespace LIBC_NAMESPACE::fixed_point
263 
264 #endif // LIBC_COMPILER_HAS_FIXED_POINT
265 
266 #endif // LLVM_LIBC_SRC___SUPPORT_FIXEDPOINT_SQRT_H
267