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