FastLED 3.10.6
Loading...
Searching...
No Matches

◆ divide64By32()

FL_FIXED_POINT_DIVIDE_INLINE u32 fl::divide64By32 ( u32 hi,
u32 lo,
u32 divisor )

(hi:lo) / divisor, for a quotient that fits 32 bits.

Knuth's Algorithm D over two base-2^16 digits, which is what lets a 64-bit numerator be divided using the 32-bit divider twice. The two correction loops run at most twice each and usually not at all; they are what make the base-2^16 estimate exact rather than approximate.

Defined, not undefined, on the two inputs the wide path leaves to the compiler: a zero divisor and a quotient too large for 32 bits both return 0xFFFFFFFF. That is a deliberate difference and is not something a caller may rely on – the wide path wraps instead – so operator/'s contract is unchanged and the divergence is pinned by a test rather than left to be discovered.

Kept out of #if FL_FIXED_POINT_NARROW_DIVIDE on purpose. Gating the definition would leave it compiled by no host and tested by nothing, which is how simd_noop.hpp went two rounds of tuning against a build that excluded it (FastLED#4216).

Definition at line 120 of file wide_divide.h.

121 {
122 constexpr u32 kBase = 65536u;
123 if (divisor == 0u || hi >= divisor) {
124 return 0xFFFFFFFFu;
125 }
126
127 // Normalising the divisor is what makes the digit estimate below good to
128 // within one, which is the whole reason two corrections suffice.
129 const int shift = detail::fixedPointLeadingZeros(divisor);
130 const u32 d = divisor << shift;
131 const u32 d_high = d >> 16;
132 const u32 d_low = d & 0xFFFFu;
133
134 // A shift of 32 is undefined, so the unshifted case is spelled out rather
135 // than folded in with a mask.
136 const u32 n_high = shift == 0 ? hi : ((hi << shift) | (lo >> (32 - shift)));
137 const u32 n_low = lo << shift;
138 const u32 n_low_high = n_low >> 16;
139 const u32 n_low_low = n_low & 0xFFFFu;
140
141 u32 quotient_high = n_high / d_high;
142 u32 remainder = n_high - quotient_high * d_high;
143 while (quotient_high >= kBase ||
144 static_cast<u64>(quotient_high) * d_low >
145 static_cast<u64>(kBase) * remainder + n_low_high) {
146 --quotient_high;
147 remainder += d_high;
148 if (remainder >= kBase) {
149 break;
150 }
151 }
152
153 // Deliberate 32-bit wraparound: the true value needs 33 bits and the
154 // low 32 are the ones the next digit is taken from.
155 const u32 partial =
156 static_cast<u32>(n_high * kBase + n_low_high - quotient_high * d);
157
158 u32 quotient_low = partial / d_high;
159 remainder = partial - quotient_low * d_high;
160 while (quotient_low >= kBase ||
161 static_cast<u64>(quotient_low) * d_low >
162 static_cast<u64>(kBase) * remainder + n_low_low) {
163 --quotient_low;
164 remainder += d_high;
165 if (remainder >= kBase) {
166 break;
167 }
168 }
169
170 return quotient_high * kBase + quotient_low;
171}
FL_FIXED_POINT_DIVIDE_INLINE int fixedPointLeadingZeros(u32 value) FL_NO_EXCEPT
Leading zeros of a non-zero 32-bit value; 32 for zero.
Definition wide_divide.h:86
fl::u64 u64
Definition stdint.h:220

References fl::detail::fixedPointLeadingZeros(), FL_FIXED_POINT_DIVIDE_INLINE, and FL_NO_EXCEPT.

Referenced by fl::s16x16::operator/(), and fl::u16x16::operator/().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: