| 1 | /// Creates a function used by some division algorithms to compute the "normalization shift". |
| 2 | #[allow (unused_macros)] |
| 3 | macro_rules! impl_normalization_shift { |
| 4 | ( |
| 5 | $name:ident, // name of the normalization shift function |
| 6 | // boolean for if `$uX::leading_zeros` should be used (if an architecture does not have a |
| 7 | // hardware instruction for `usize::leading_zeros`, then this should be `true`) |
| 8 | $use_lz:ident, |
| 9 | $n:tt, // the number of bits in a $iX or $uX |
| 10 | $uX:ident, // unsigned integer type for the inputs of `$name` |
| 11 | $iX:ident, // signed integer type for the inputs of `$name` |
| 12 | $($unsigned_attr:meta),* // attributes for the function |
| 13 | ) => { |
| 14 | /// Finds the shift left that the divisor `div` would need to be normalized for a binary |
| 15 | /// long division step with the dividend `duo`. NOTE: This function assumes that these edge |
| 16 | /// cases have been handled before reaching it: |
| 17 | /// ` |
| 18 | /// if div == 0 { |
| 19 | /// panic!("attempt to divide by zero") |
| 20 | /// } |
| 21 | /// if duo < div { |
| 22 | /// return (0, duo) |
| 23 | /// } |
| 24 | /// ` |
| 25 | /// |
| 26 | /// Normalization is defined as (where `shl` is the output of this function): |
| 27 | /// ` |
| 28 | /// if duo.leading_zeros() != (div << shl).leading_zeros() { |
| 29 | /// // If the most significant bits of `duo` and `div << shl` are not in the same place, |
| 30 | /// // then `div << shl` has one more leading zero than `duo`. |
| 31 | /// assert_eq!(duo.leading_zeros() + 1, (div << shl).leading_zeros()); |
| 32 | /// // Also, `2*(div << shl)` is not more than `duo` (otherwise the first division step |
| 33 | /// // would not be able to clear the msb of `duo`) |
| 34 | /// assert!(duo < (div << (shl + 1))); |
| 35 | /// } |
| 36 | /// if full_normalization { |
| 37 | /// // Some algorithms do not need "full" normalization, which means that `duo` is |
| 38 | /// // larger than `div << shl` when the most significant bits are aligned. |
| 39 | /// assert!((div << shl) <= duo); |
| 40 | /// } |
| 41 | /// ` |
| 42 | /// |
| 43 | /// Note: If the software bisection algorithm is being used in this function, it happens |
| 44 | /// that full normalization always occurs, so be careful that new algorithms are not |
| 45 | /// invisibly depending on this invariant when `full_normalization` is set to `false`. |
| 46 | $( |
| 47 | #[$unsigned_attr] |
| 48 | )* |
| 49 | fn $name(duo: $uX, div: $uX, full_normalization: bool) -> usize { |
| 50 | // We have to find the leading zeros of `div` to know where its msb (most significant |
| 51 | // set bit) is to even begin binary long division. It is also good to know where the msb |
| 52 | // of `duo` is so that useful work can be started instead of shifting `div` for all |
| 53 | // possible quotients (many division steps are wasted if `duo.leading_zeros()` is large |
| 54 | // and `div` starts out being shifted all the way to the msb). Aligning the msbs of |
| 55 | // `div` and `duo` could be done by shifting `div` left by |
| 56 | // `div.leading_zeros() - duo.leading_zeros()`, but some CPUs without division hardware |
| 57 | // also do not have single instructions for calculating `leading_zeros`. Instead of |
| 58 | // software doing two bisections to find the two `leading_zeros`, we do one bisection to |
| 59 | // find `div.leading_zeros() - duo.leading_zeros()` without actually knowing either of |
| 60 | // the leading zeros values. |
| 61 | |
| 62 | let mut shl: usize; |
| 63 | if $use_lz { |
| 64 | shl = (div.leading_zeros() - duo.leading_zeros()) as usize; |
| 65 | if full_normalization { |
| 66 | if duo < (div << shl) { |
| 67 | // when the msb of `duo` and `div` are aligned, the resulting `div` may be |
| 68 | // larger than `duo`, so we decrease the shift by 1. |
| 69 | shl -= 1; |
| 70 | } |
| 71 | } |
| 72 | } else { |
| 73 | let mut test = duo; |
| 74 | shl = 0usize; |
| 75 | let mut lvl = $n >> 1; |
| 76 | loop { |
| 77 | let tmp = test >> lvl; |
| 78 | // It happens that a final `duo < (div << shl)` check is not needed, because the |
| 79 | // `div <= tmp` check insures that the msb of `test` never passes the msb of |
| 80 | // `div`, and any set bits shifted off the end of `test` would still keep |
| 81 | // `div <= tmp` true. |
| 82 | if div <= tmp { |
| 83 | test = tmp; |
| 84 | shl += lvl; |
| 85 | } |
| 86 | // narrow down bisection |
| 87 | lvl >>= 1; |
| 88 | if lvl == 0 { |
| 89 | break |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | // tests the invariants that should hold before beginning binary long division |
| 94 | /* |
| 95 | if full_normalization { |
| 96 | assert!((div << shl) <= duo); |
| 97 | } |
| 98 | if duo.leading_zeros() != (div << shl).leading_zeros() { |
| 99 | assert_eq!(duo.leading_zeros() + 1, (div << shl).leading_zeros()); |
| 100 | assert!(duo < (div << (shl + 1))); |
| 101 | } |
| 102 | */ |
| 103 | shl |
| 104 | } |
| 105 | } |
| 106 | } |
| 107 | |